slab / quill

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

Complex Blots composing #1163

Closed artaommahe closed 9 months ago

artaommahe commented 7 years ago

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

benbro commented 7 years ago

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

artaommahe commented 7 years ago

@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 commented 7 years ago

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

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

benbro commented 7 years ago

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:

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

artaommahe commented 7 years ago

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 commented 7 years ago

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.

jhchen commented 7 years ago

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.

artaommahe commented 7 years ago

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

divyenduz commented 7 years ago

+1

sankalpsans commented 7 years ago

+1 for having a good way of doing this.

gztchan commented 7 years ago

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.

seandearnaley commented 7 years ago

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.

vasiltabakov commented 7 years ago

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

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.

benjismith commented 7 years ago

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

vasiltabakov commented 7 years ago

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

vasiltabakov commented 7 years ago

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

benjismith commented 7 years ago

Niiiiice. Thank you!!

Black-Stork commented 6 years ago

@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

victor-andreescu commented 6 years ago

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

vasiltabakov commented 6 years ago

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.

victor-andreescu commented 6 years ago

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!

charrondev commented 6 years ago

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.

stefanocdn commented 6 years ago

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

1c7 commented 6 years ago

@harrondev Hi, any update on that Blog post?

a2xchip commented 6 years ago

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

Easy-Hexford commented 5 years ago

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

vinitpradhn18 commented 5 years ago

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

skxctech commented 5 years ago

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.

tranduongms1 commented 5 years ago

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

DoctorsInTech commented 4 years ago

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.

a2xchip commented 4 years ago

@DoctorsInTech I have implemented it. I am sure my solution is not reusable but, might be used as example... Will check and ping back

alihaddadkar commented 3 years ago

Did anyone find a solution to the problem? (image+caption like medium)

braebo commented 3 years ago

A 4 year old thread about image captions! I'm impressed with how complex something like a rich text editor is. I hope there will be an official solution or plug-in for this some day.