slab / quill

Quill is a modern WYSIWYG editor built for compatibility and extensibility

Home Page:https://quilljs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Option to insert an image from a URL

jasongisstl opened this issue · comments

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.

Any progress on this?

Also interested in this :)

[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...

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?

commented

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

Still no update :(

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

commented

+6666

+1

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

+1 how is this not a feature yet?

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

+1 my team would love this feature.

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

Keeping the fire alive for this request 🔥

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

commented

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

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);
            }
        }

Any progress on this?

Hey there! Any progress?

commented

@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.

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

commented

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

=> Translate it into a js function

I posted a solution above

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);
            }
        }

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>

@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');
  }

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

@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();
};

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

Any Update on this ?

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:";
}

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

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,
    },
  },
};
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?

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,