quill icon indicating copy to clipboard operation
quill copied to clipboard

Complex Blots composing

Open artaommahe opened this issue 8 years ago • 33 comments

Cant find proper way to create complex Blot or Blots composing for some use-cases due to one way Quill-Blots interaction. Real example - medium-like image/video with optional caption.

<!-- {{ smth }} is just marks for values places -->
<div class="article__figure" contenteditable="false">
    <!-- wrapper with image or video here -->
    <div class="article__image">
        <img src="{{ src }}" />
    </div>

    <!-- and different ways to edit caption - via contenteditable div or textarea -->
    <div class="article__caption" contenteditable="true">
        {{ caption }}
    </div>
    <!-- or -->
    <div class="article__caption">
        <!-- view mode only, created in static create() by default -->
        {{ caption }}
        <!-- edit mode only, replaces plain text in constructor() -->
        <textarea rows="1">{{ caption }}</textarea>
    </div>
</div>

This cant be created via one Blot cause there is no way to update Blot value from inner events (e.x. textarea value changing). I cant find how this can be solved via separate Blots - Figure, Image/Video and Caption. Cause this composition should behave as a whole - remove whole block on remove actions, dont allow to remove block parts only (image/caption). Also there is an issue with this block value - it should be one Delta element-value like { src: string, caption?: string } cause to proper Delta parsing for other app cases (captions should be bound directly to specific image/video without additional parsing to proper extract image/video data, e.x. i want to get first 5 images from article with their captions to separate displaying).

artaommahe avatar Dec 05 '16 13:12 artaommahe

Your code example has nothing to do with Quill. Please include a simple working example to show your issue.

benbro avatar Dec 05 '16 13:12 benbro

@benbro as i sad currently i cant achieve necessary behavior with Quill and such markup so i cant create working Blot. This example is real markup that i need to paste on image insert in quill editor, so it should be some Blot or several Blots. Now i have this as image Blot

const FIGURE_CLASS = 'article__figure';
const IMAGE_WRAPPER_CLASS = 'article__image';

export class ImageBlockBlot extends BlockEmbed {

  public static blotName = 'imageBlock';
  public static tagName = 'div';
  public static className = FIGURE_CLASS;


  public static create(value: string): Node {

    const figureElement = <HTMLDivElement> super.create(value);


    const imageWrapperElement = document.createElement('div');

    imageWrapperElement.classList.add(IMAGE_WRAPPER_CLASS);


    const imgElement = document.createElement('img');

    const sanitizedValue = ImageBlot.sanitize(value);

    imgElement.setAttribute('src', sanitizedValue);


    imageWrapperElement.appendChild(imgElement);

    figureElement.appendChild(imageWrapperElement);

    return figureElement;
  }

  public static value(divElement: HTMLDivElement): string {

    const imageElement = divElement.querySelector('img');

    if (!imageElement) return null;

    const value = imageElement.getAttribute('src');

    return value;
  }
}

This renders only image part without editable comment field. And i cant understand how to add editable comment field here that will be part of ImageBlock Blot with final Blot value format { src: string, caption?: string }

artaommahe avatar Dec 05 '16 15:12 artaommahe

What i want to achieve - markup and behavior like medium editor images image

figure.graf.graf--figure equals .article__figure in first post

artaommahe avatar Dec 05 '16 15:12 artaommahe

You are trying to create a Blot that has an image and a caption and make the caption editable inline?

How will the user add a new image? How will the user change the image?

Not sure if it's possible to make the caption editable inline ( @jhchen ?)

You can create an image tooltip that has two fields:

  • file/url field for the image
  • caption field

That will allow you to add a new image with a caption, replace an image and edit a caption.

benbro avatar Dec 05 '16 15:12 benbro

You are trying to create a Blot that has an image and a caption and make the caption editable inline?

yes, on button click user adds image link and see smth like on screenshot above where he can edit optional caption

How will the user add a new image? How will the user change the image?

image can be changed only via remove current(whole block)/add new. It's important to have caption easy editable and not so important for added image. Also there is issue with editing caption via tooltip - it cannot be formatted. It would be usefull to have ability to format caption text - mark part as bold or as link (medium editor allows this)

artaommahe avatar Dec 05 '16 15:12 artaommahe

More about tooltip solution - it does not allow to edit image/caption cause we need to handle image or caption nodes click to show tooltip. Only Blot has access to this nodes, but it cant call anything from Quill instance or somewhere else to show tooltip with image source and caption to edit it. Same issue with Link format - it cant be editable now for same reason and requires to remove/add new link for this case.

artaommahe avatar Dec 06 '16 12:12 artaommahe

Not sure if it's possible to make the caption editable inline

Embed blots can have arbitrary content, editable or not. The formula blot makes use of this feature.

jhchen avatar Dec 06 '16 20:12 jhchen

@jhchen also is there any support for nested complex blots? Currently i'm working on spoiler block that hides content (any other blots including embeds) and dont see any way to implement spoilers nesting - Delta format has plain structure and looks like it's impossible with it. Why Delta doesn't support nesting?

artaommahe avatar Mar 18 '17 21:03 artaommahe

+1

divyenduz avatar May 12 '17 11:05 divyenduz

+1 for having a good way of doing this.

sankalpsans avatar May 15 '17 07:05 sankalpsans

I am also doing the same thing, but it's hard for developer to create complex blots, including how to handler events(like pressing enter at the end of caption or pressing backspace/delete at the beginning of caption) in complex blot, how to make delta support complex change, etc.

gztchan avatar May 24 '17 08:05 gztchan

I've also spent a remarkable amount of time trying to get an editable figure figcaption to work and its really been disheartening... I've been unable find any solution that works let alone practical solutions. It seems like all the most obvious things you'd extend Quill with are incredibly difficult to implement.

seandearnaley avatar Jul 21 '17 04:07 seandearnaley

Hi everyone, had a similar problem and created this:

quill-caption-test

The delta in the resulting content of the editor correctly contains the new caption of the image. This is part of an Angular project so the code of the module was written in Typescript.

To provide some more insight of how this works

  • there's a custom Embed containing an image and a caption elements
  • what you see in the GIF is: upon pasting an image from the clipboard the editor inserts that custom embed
  • the image options module (delete image and/or change caption) is a separate module triggering when a user clicks on the custom embed

Since in our case we want the caption to link to an external image we've decided to have the input at the top of the image, but of course it's easy enough to change it's position and style to match the actual caption below the image.

If anyone's interested we can publish it on Github as a proper stand-alone module.

vasiltabakov avatar Sep 30 '17 09:09 vasiltabakov

Hey @vasiltabakov, I'd love to see your code. Could you post a gist or something?

benjismith avatar Oct 03 '17 16:10 benjismith

Will do, need to remove a lot of ad-hoc code and will post the typescript version today-ish

vasiltabakov avatar Oct 06 '17 04:10 vasiltabakov

Here you go: https://gist.github.com/vasiltabakov/a3bf16f6e69da421cbff60389b9eb975

vasiltabakov avatar Oct 07 '17 09:10 vasiltabakov

Niiiiice. Thank you!!

benjismith avatar Oct 09 '17 01:10 benjismith

@artaommahe Hello! I hope you did this task and you can help me. Because I also want to do a figure blot with image and caption parts but I can't do that like at telegraph:

<wrapper>
    <img />
    <caption />
</wrapper>

Could you help me? I'm fighting with this problem during a week

Black-Stork avatar Feb 05 '18 23:02 Black-Stork

@vasiltabakov I'm also in the search of a solution for inserting figure with img + figcaption with Quill. I tried your code and it renders ok but the image and the caption inside TaskerFigure doesn't seem to be blots so inline editing for the caption is not an option. Probably because when they are created you used JS methods not Quill?. Any idea how to make them blots?

victor-andreescu avatar Feb 07 '18 12:02 victor-andreescu

Mine was a very ad-hoc solution. But I think the whole thing should be a Blot, because if they're separate you'll be able to delete one while the other remains. I doubt that's what you'd want. Inline editing should be easy with some more JS code, you can insert an input when the caption is clicked (and remove all my code for the 'border' and caption input header etc. Many options, depending on what exactly you want.

vasiltabakov avatar Feb 07 '18 12:02 vasiltabakov

I was thinking of allowing the deletion of the caption but you are right it's better of your way. I will try the caption on click suggestion hopefully I will manage to bypass the "delete blot on backspace" behavior for the caption. Thanks!

victor-andreescu avatar Feb 07 '18 13:02 victor-andreescu

I'll have some new implementations of some rather complex custom blots and behaviours in an open source project from my work soon. I'll likely write a Blog post as well for it and I'll update here when it's done.

charrondev avatar Feb 28 '18 05:02 charrondev

@charrondev that would be very nice of you and very helpful. I am also trying to use a custom blot with an editable caption, but it is pretty difficult in my opinion.

stefanocdn avatar Apr 05 '18 21:04 stefanocdn

@harrondev Hi, any update on that Blog post?

1c7 avatar Aug 22 '18 07:08 1c7

I do recommend to check out thing telegra.ph does https://telegra.ph/

a2xchip avatar Oct 28 '18 20:10 a2xchip

@benbro as i sad currently i cant achieve necessary behavior with Quill and such markup so i cant create working Blot. This example is real markup that i need to paste on image insert in quill editor, so it should be some Blot or several Blots. Now i have this as image Blot

const FIGURE_CLASS = 'article__figure';
const IMAGE_WRAPPER_CLASS = 'article__image';

export class ImageBlockBlot extends BlockEmbed {

  public static blotName = 'imageBlock';
  public static tagName = 'div';
  public static className = FIGURE_CLASS;


  public static create(value: string): Node {

    const figureElement = <HTMLDivElement> super.create(value);


    const imageWrapperElement = document.createElement('div');

    imageWrapperElement.classList.add(IMAGE_WRAPPER_CLASS);


    const imgElement = document.createElement('img');

    const sanitizedValue = ImageBlot.sanitize(value);

    imgElement.setAttribute('src', sanitizedValue);


    imageWrapperElement.appendChild(imgElement);

    figureElement.appendChild(imageWrapperElement);

    return figureElement;
  }

  public static value(divElement: HTMLDivElement): string {

    const imageElement = divElement.querySelector('img');

    if (!imageElement) return null;

    const value = imageElement.getAttribute('src');

    return value;
  }
}

This renders only image part without editable comment field. And i cant understand how to add editable comment field here that will be part of ImageBlock Blot with final Blot value format { src: string, caption?: string }

I've tried your way. but if i set tagName as p, the content will be strange, and i'll get many p tags.

Easy-Hexford avatar Dec 09 '18 09:12 Easy-Hexford

Has anyone figured out an ideal solution to this problem? I have been able to achieve the same (figure with editable caption). I have implemented the functionality and the html generated is fine, but the delta generated is not appropriate. When I re-render the sample blot in editor through delta, it fails. This is how I have I have implemented #2437 . Any help is appreciated

vinitpradhn18 avatar Dec 24 '18 11:12 vinitpradhn18

Anyone ever found a decent solution for this? Quill confuses the hell out of me, I can build a completely configurable complex Blot, but I have to clue how/where to instruct Quill on how to render it from ops when re-editing that Blot. I'm on the edge of building post-render functions that dig into the HTML and just re-build the areas that have my custom Blots/ops.

skxctech avatar Jun 24 '19 16:06 skxctech

This is my solution, other embed card can refer. https://gist.github.com/tranduongms1/584d43ec7d8ddeab458f087adbeef950

tranduongms1 avatar Jul 27 '19 05:07 tranduongms1

I do recommend to check out thing telegra.ph does https://telegra.ph/

Hi @a2xchip, do you know if the source code is available for telegra.ph, their solution works very well and I would be interested to know how they had solved it.

DoctorsInTech avatar Mar 02 '20 14:03 DoctorsInTech