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

Option to insert an image from a URL #893

Open
jasongisstl opened this issue Aug 26, 2016 · 38 comments
Open

Option to insert an image from a URL #893

jasongisstl opened this issue Aug 26, 2016 · 38 comments
Labels

Comments

@jasongisstl
Copy link

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
Copy link

laosb commented Jan 23, 2017

Any progress on this?

@CoenWarmer
Copy link

Also interested in this :)

@dennisdebel
Copy link

dennisdebel commented Feb 22, 2017

[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: #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
Copy link

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
Copy link

masza commented May 11, 2017

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

@cuduy197
Copy link

Still no update :(

@McSneaky
Copy link

McSneaky commented May 24, 2017

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
Copy link

Matoo125 commented Jul 3, 2017

I think here #863 (comment) it is written pretty well. All you need to do is to create custom handler

@vhq2016
Copy link

vhq2016 commented Nov 24, 2017

+6666

@oliveratgithub
Copy link

+1

@tedtoy
Copy link

tedtoy commented Feb 13, 2018

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

@rodolfosaraiva
Copy link

+1

@Vice93
Copy link

Vice93 commented Jul 18, 2018

+1 how is this not a feature yet?

@davek1979
Copy link

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

@owensco
Copy link

owensco commented Sep 28, 2018

+1 my team would love this feature.

@alexjustesen
Copy link

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

@alexandercsmith
Copy link

Keeping the fire alive for this request 🔥

@oliveratgithub
Copy link

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

@repat
Copy link

repat commented Feb 27, 2019

We got it working by extracting the base64 encoded image, storing it on the hard drive and replacing the src="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 #1449.

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

@Gib-git
Copy link

Gib-git commented Apr 15, 2019

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
Copy link

Any progress on this?

@siberiadev
Copy link

Hey there! Any progress?

@repat
Copy link

repat commented Apr 23, 2019

@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
Copy link

siberiadev commented Apr 23, 2019

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

@repat
Copy link

repat commented Apr 23, 2019

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

=> Translate it into a js function

@Gib-git
Copy link

Gib-git commented Apr 23, 2019

I posted a solution above

@jtlimson
Copy link

jtlimson commented Jul 5, 2019

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
Copy link

chrisbmoore commented Mar 25, 2020

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
Copy link

@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
Copy link

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

@digilist
Copy link

digilist commented Aug 5, 2020

@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
Copy link

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

@joeabn
Copy link

joeabn commented Nov 21, 2020

Any Update on this ?

@connor4312
Copy link

connor4312 commented Mar 18, 2021

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
Copy link

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
Copy link

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
Copy link

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
Copy link

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,

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

No branches or pull requests