slab / quill

Quill is a modern WYSIWYG editor built for compatibility and extensibility
https://quilljs.com
BSD 3-Clause "New" or "Revised" License
43.35k stars 3.37k forks source link

Option to insert an image from a URL #893

Open jasongisstl opened 8 years ago

jasongisstl commented 8 years ago

The Image button on the toolbar allows you to insert an image from your local machine. It would be brilliant if this could be extended to also allow an image URL to be provided.

It is possible to drag & drop an image from the web, but this may not be obvious to all users. Also, in our particular case, we have custom drag & drop handling which prevents this from working in our applications.

Version: 1.0.0-rc2

P.S. Thank you for all your hard work on 1.0 - it really is fantastic.

laosb commented 7 years ago

Any progress on this?

CoenWarmer commented 7 years ago

Also interested in this :)

dennisdebel commented 7 years ago

[edit]Best Approach is something like the adding a matcher to the clipboard module, watching for copypasted urls and converting them to images (new Delta().insert({image: url}); works) like the code found here: https://github.com/quilljs/quill/issues/109 by @schneidmaster Couldnt get it to works for images still tho...learning...

[edit 2] this custom matcher converts urls to links and in case they link to image, converts the urls to inline images (put it in quill.on('text-change', function(delta, oldDelta, source) {)


var Delta = Quill.import('delta');

//convert img links to actual images
quill.clipboard.addMatcher(Node.TEXT_NODE, function(node, delta) {
    var regex = /https?:\/\/[^\s]+/g;
        if(typeof(node.data) !== 'string') return;
            var matches = node.data.match(regex);

            if(matches && matches.length > 0) {
                var ops = [];
                var str = node.data;

                matches.forEach(function(match) {
                     var split = str.split(match);
                     if(match.match(/\.(png|jpg|jpeg|gif)$/)!=null){
                         var beforeLink = split.shift();                     
                         ops.push({ insert: beforeLink });
                         ops.push({ insert:{image: match}, attributes: { link: match } 
                      });

                    str = split.join(match);                    
                    } else { //if link is not an image
                        var beforeLink = split.shift();
                        ops.push({ insert: beforeLink });                        
                        ops.push({ insert: match, attributes: { link: match } 
                    }); 
                        str = split.join(match);
                    }                                                     
    });
                ops.push({ insert: str });
                delta.ops = ops;
             }                   
    return delta;
});

~~Obviously the wrong approach > I tried to implement this in the form of a custom module, but my JS skills suck. Behaviour im looking for is to replace all urls containing images to 'inline' images (like the etherpad image plugin). I made (read: copypasta) an example that sorta works here: http://codepen.io/anon/pen/PpYjxG In the example, type a bit and Copy/Paste an image url in the editor field and it will appear as inline image. Only after that the ability to edit/type stops, which must be something obvious, but as I said..major noob...~~

tannerjt commented 7 years ago

Would love to see this as well. As a side note, this was how images were handled in the early stages of QuillJS. Any reason why this was removed?

masza commented 7 years ago

I don't understand why there is base64 upload images and no support for third-party image upload. This is VERY important!

cuduy197 commented 7 years ago

Still no update :(

McSneaky commented 7 years ago

I got it working like this: https://stackoverflow.com/questions/43857373/quill-editor-vuejs2-image-upload-base64-image-to-url Not directly from clipboard tho

Matoo125 commented 7 years ago

I think here https://github.com/quilljs/quill/issues/863#issuecomment-240579430 it is written pretty well. All you need to do is to create custom handler

vhq2016 commented 6 years ago

+6666

oliveratgithub commented 6 years ago

+1

tedtoy commented 6 years ago

I too would like to make public my desire for such a feature.

rodolfosaraiva commented 6 years ago

+1

Vice93 commented 6 years ago

+1 how is this not a feature yet?

davek1979 commented 6 years ago

Could someone from the team maybe shed some light on this please? This feature is really, REALLY needed.

owensco commented 6 years ago

+1 my team would love this feature.

alexjustesen commented 5 years ago

For what it's worth... would be really really nice to have...

alexandercsmith commented 5 years ago

Keeping the fire alive for this request 🔥

oliveratgithub commented 5 years ago

I toss some bricks into that fire so it keeps burning longer

repat commented 5 years ago

We got it working by extracting the base64 encoded image, storing it on the hard drive and replacing thesrc="data..." code with src="/folder/file..." but quill doesn't display the picture when the HTML is loaded with dangerouslyPasteHTML the next time we open it, see https://github.com/quilljs/quill/issues/1449.

+1 would also like to see this feature built-in

Gibigbig commented 5 years ago

var toolbarOptions = [
            ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
            ['blockquote', 'code-block'],

            [{ 'header': 1 }, { 'header': 2 }],               // custom button values
            [{ 'list': 'ordered' }, { 'list': 'bullet' }],
            [{ 'script': 'sub' }, { 'script': 'super' }],      // superscript/subscript
            [{ 'indent': '-1' }, { 'indent': '+1' }],          // outdent/indent
            [{ 'direction': 'rtl' }],                         // text direction

            [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
            [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

            [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
            [{ 'font': [] }],
            [{ 'align': [] }],

            ['clean'],                                         // remove formatting button
            ['image']
        ];

        var quill = new Quill('#editor', {
            theme: 'snow',
            modules: {
                toolbar: {
                    container: toolbarOptions,
                    handlers: {
                        image: imageHandler
                    }
                }
            },
        });

        function imageHandler() {
            var range = this.quill.getSelection();
            var value = prompt('What is the image URL');
            if(value){
                this.quill.insertEmbed(range.index, 'image', value, Quill.sources.USER);
            }
        }
jiangqiming commented 5 years ago

Any progress on this?

å•¥

siberiadev commented 5 years ago

Hey there! Any progress?

repat commented 5 years ago

@siberiadev in case you're working with laravel, for extracting the image you can use extract_inline_img() from my package here: https://packagist.org/packages/repat/laravel-helper

Or just have a look at the function here and extract what you need.

In terms of displaying I haven't seen any progress.

siberiadev commented 5 years ago

@repat, thank you. I use Node.js for backend.

repat commented 5 years ago

PHP and Laravel (MVC, similar to express I think) is fairly eary to read so you could:

=> Translate it into a js function

Gibigbig commented 5 years ago

I posted a solution above

jtlimson commented 5 years ago

This works like a charm

var toolbarOptions = [
            ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
            ['blockquote', 'code-block'],

            [{ 'header': 1 }, { 'header': 2 }],               // custom button values
            [{ 'list': 'ordered' }, { 'list': 'bullet' }],
            [{ 'script': 'sub' }, { 'script': 'super' }],      // superscript/subscript
            [{ 'indent': '-1' }, { 'indent': '+1' }],          // outdent/indent
            [{ 'direction': 'rtl' }],                         // text direction

            [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
            [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

            [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
            [{ 'font': [] }],
            [{ 'align': [] }],

            ['clean'],                                         // remove formatting button
            ['image']
        ];

        var quill = new Quill('#editor', {
            theme: 'snow',
            modules: {
                toolbar: {
                    container: toolbarOptions,
                    handlers: {
                        image: imageHandler
                    }
                }
            },
        });

        function imageHandler() {
            var range = this.quill.getSelection();
            var value = prompt('What is the image URL');
            if(value){
                this.quill.insertEmbed(range.index, 'image', value, Quill.sources.USER);
            }
        }
chrisbmoore commented 4 years ago

For anyone coming here using ngx-quill:

import { QuillModules, defaultModules } from 'ngx-quill';

export class MyComponent {

    quillModules: QuillModules = {
    toolbar: {
      container: defaultModules.toolbar, 
      handlers: {
        image: imageHandler
      }
    }
  };

}

imageHandler(this: any) {
    const tooltip = this.quill.theme.tooltip;
    const originalSave = tooltip.save;
    const originalHide = tooltip.hide;
    tooltip.save = function(this: any) {
      const range = this.quill.getSelection(true);
      const value = this.textbox.value;
      if (value) {
        this.quill.insertEmbed(range.index, 'image', value, 'user');
      }
    };
    // Called on hide and save.
    tooltip.hide = function (this: any) {
       tooltip.save = originalSave;
       tooltip.hide = originalHide;
       tooltip.hide();
    };
    tooltip.edit('image');
    tooltip.textbox.placeholder = "Embed URL";
  }

<quill-editor [modules]="quillModules"></quill-editor>

pburrows commented 4 years ago

@chrisbmoore this works great for ngx-quill! However, it clobbers the tooltip saves for links and videos. Need to set the original save back when done. Instead of this:

function imageHandler(this: any) {
  const tooltip = this.quill.theme.tooltip;
  tooltip.save = function (this: any) {
    const range = this.quill.getSelection(true);
    const value = this.textbox.value;
    if (value) {
      this.quill.insertEmbed(range.index, 'image', value, 'user');
    }
  }
  tooltip.edit('image');
}

do something like this:

imageHandler(this: any) {
    const tooltip = this.quill.theme.tooltip;
    const originalSave = tooltip.save;
    tooltip.save = function(this: any) {
      const range = this.quill.getSelection(true);
      const value = this.textbox.value;
      if (value) {
        this.quill.insertEmbed(range.index, 'image', value, 'user');
      }
      tooltip.save = originalSave;
    };
    tooltip.edit('image');
  }
chrisbmoore commented 4 years ago

@pburrows You're right, the original save function should be reset. Thanks!

digilist commented 4 years ago

@pburrows @chrisbmoore If the tooltip is closed without clicking "Save" the original save function is not restored, as the save method is not called. Do you have any idea how to solve this? I did not find some callback that is called in that case.

Update: I found a solution myself by overriding tooltip.hide() with the restore code:

tooltip.hide = function(this: any) {
    tooltip.save = originalSave;
    tooltip.hide = originalHide;
    tooltip.hide();
};
chrisbmoore commented 4 years ago

@digilist Thanks for the correction! Yep your solution makes sense and I'll add it to my code.

joeabn commented 3 years ago

Any Update on this ?

connor4312 commented 3 years ago

I tried to find a better way to do this... the tooltips in Quill are hidden quite effectively so that consumers can't extend on them cleanly.

Anyway, one more bit that you'll need to customize the label is this CSS snippet:

.ql-tooltip[data-mode=image]::before {
  content: "Enter image URL:";
}
tranduclinh2067 commented 2 years ago

I tried to find a better way to do this... the tooltips in Quill are hidden quite effectively so that consumers can't extend on them cleanly.

Anyway, one more bit that you'll need to customize the label is this CSS snippet:

.ql-tooltip[data-mode=image]::before {
  content: "Enter image URL:";
}

It doesn't work

Milutin-P commented 2 years ago

Other solutions did not work for me :( I'm using a "react-quill": "^1.3.5", So I wrote it like this:

function imageHandler() {
  const { tooltip } = this.quill.theme;
  const originalSave = tooltip.save;
  const originalHide = tooltip.hide;

  tooltip.save = () => {
    const range = this.quill.getSelection(true);
    const { value } = tooltip.textbox;
    if (isValidURL(value)) { //isValidURL is a validator
      this.quill.insertEmbed(range.index, 'image', value, Quill.sources.USER);
    } else {
      // error message handler
    }
  };

  // Called on hide and save.
  tooltip.hide = () => {
    tooltip.save = originalSave;
    tooltip.hide = originalHide;
    tooltip.hide();
  };

  tooltip.edit('image');
  tooltip.textbox.placeholder = 'Embed URL';
}
const modules = {
  toolbar: {
    container: [
      [{ header: '1' }, { header: '2' }, { font: [] }],
      [{ size: [] }],
      ['bold', 'italic', 'underline', 'strike', 'blockquote'],
      [{ list: 'ordered' }, { list: 'bullet' }],
      ['link', 'image', 'video'],
    ],
    handlers: {
      image: imageHandler,
    },
  },
};
smujaddid commented 2 years ago
var toolbarOptions = [
            ['bold', 'italic', 'underline', 'strike'],        // toggled buttons
            ['blockquote', 'code-block'],

            [{ 'header': 1 }, { 'header': 2 }],               // custom button values
            [{ 'list': 'ordered' }, { 'list': 'bullet' }],
            [{ 'script': 'sub' }, { 'script': 'super' }],      // superscript/subscript
            [{ 'indent': '-1' }, { 'indent': '+1' }],          // outdent/indent
            [{ 'direction': 'rtl' }],                         // text direction

            [{ 'size': ['small', false, 'large', 'huge'] }],  // custom dropdown
            [{ 'header': [1, 2, 3, 4, 5, 6, false] }],

            [{ 'color': [] }, { 'background': [] }],          // dropdown with defaults from theme
            [{ 'font': [] }],
            [{ 'align': [] }],

            ['clean'],                                         // remove formatting button
            ['image']
        ];

        var quill = new Quill('#editor', {
            theme: 'snow',
            modules: {
                toolbar: {
                    container: toolbarOptions,
                    handlers: {
                        image: imageHandler
                    }
                }
            },
        });

        function imageHandler() {
            var range = this.quill.getSelection();
            var value = prompt('What is the image URL');
            if(value){
                this.quill.insertEmbed(range.index, 'image', value, Quill.sources.USER);
            }
        }

Inserting images with insertEmbed for images with URL does work, but quill doesn't allow it to be deleted, like when I press backspace or delete key, the inserted image won't disappear. Any way to solve this?

jaymathew commented 1 year ago

Thanks @Milutin-P, your code helped in 1.3.5, unfortunately something changed on 1.3.7 and now your code snippet doesn't work. I think the issue lies with trying to get the tooltip. const { tooltip } = this.quill.theme I'm not sure if something changed but was wondering if you could shed some light on this issue.

thanks,