Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Nested Blot? #30

Closed
gztchan opened this issue May 17, 2017 · 7 comments
Closed

Nested Blot? #30

gztchan opened this issue May 17, 2017 · 7 comments

Comments

@gztchan
Copy link

gztchan commented May 17, 2017

How can i customize a new Blot that:

  1. tagName is "DIV"
  2. There is an "img" and a "p" inside

What I want to do is build different customized Blot to extend the editor.

@jhchen
Copy link
Member

jhchen commented May 17, 2017

There are lots of ways to do this depending what you want specifically. What's the use case?

@gztchan
Copy link
Author

gztchan commented May 17, 2017

import Embed from '../../quill/blots/embed';

class Caption extends Embed {
  static create(value) {
    let node = super.create(value);
    node.innerText = value;
    return node;
  }

  value() {
    return this.domNode.innerText;
  }
}

Caption.blotName = 'caption';
Caption.tagName = 'SPAN';

export default Caption;
import Block from '../../quill/blots/block';
import Embed from '../../quill/blots/embed';
import { sanitize } from '../../quill/formats/link';

class SingleImageBlot extends Block {
  static create(value) {
    let node = super.create(value);
    // node.setAttribute('contenteditable', false);
    return node;
  }

  constructor(domNode, value) {
    super(domNode, value);
    const image = Parchment.create('image', value);
    const caption = Parchment.create('caption', 'placeholder');
    this.appendChild(image);
    this.appendChild(caption);
  }

  static match(url) {
    return /\.(jpe?g|gif|png)$/.test(url) || /^data:image\/.+;base64/.test(url);
  }

  static sanitize(url) {
    return sanitize(url, ['http', 'https', 'data']) ? url : '//:0';
  }

  deleteAt(index, length) {
    this.removeChild(this.children.head);
    removeElements(this);
    super.deleteAt(index, length);
  }
}

function removeElements(blot) {
  const list = blot.children;
  while (list.head) {
    blot.removeChild(list.head);
  }
}

SingleImageBlot.blotName = 'single-image';
SingleImageBlot.className = 'ql-editor-single-image';
SingleImageBlot.tagName = 'DIV';

export default SingleImageBlot;

issue:

image
(blank place between icons is trigger)

I am not sure the way I built SingleImageBlot is correct or not (create sub-blot), and I found that when cursor is between the caption, and then press enter, content get disordered. In addition, deletion of caption doesn't work any more. So, I really wonder that how can I handle keyboard events in nested blot, and is there any examples or best practices recommended?

@jhchen
Copy link
Member

jhchen commented May 17, 2017

You have not answered the question about your use case so since I don't know what you are trying to achieve, I don't know if it is correct or not either.

With regards to your specific question about keyboard events, that is outside of the scope of Parchment. Parchment is just the data model. You might want to look at Quill for a fuller editor.

@jhchen jhchen closed this as completed May 17, 2017
@gztchan
Copy link
Author

gztchan commented May 18, 2017

Actually, I want a high customized editor, including blots, modules, themes, based on Quill's system, when I make a new blot, I find that the document about Parchment is not that helpful.

Thank you for helping me comprehend the whole library, and I will keep watching about that. 👍

@johnking
Copy link

@gztchan @jhchen

I have a similar requirement as @gztchan. In my case, I want to create a document from a JSON file, which has the document structure:

   Page -> Paragraph(s) -> sentence(s) -> words. 

For Page, I can use scroll blot of quill editor to represent it.
For Word of each sentence, I can use text blot to represent it.

but I need blots to represent sentences and paragraphs, because there are other useful information belong to these granular level such as the start-time of the sentence, end-time etc...

  1. I want to create a sentence blot based on inline blot, which host all the words of it.
  2. I want to create a paragraph blot based on block blot, which host all the sentences of it.

but I don't know how to insert/add sentenceBlot and paragraphBlot into the quill editor. There are only insertText and insertEmbed APIs available.

May you please give me any suggestions on my use case?

thanks a lot.
-John

@kilgarenone
Copy link

@jhchen
I couldn't find any documentation on how to create complex nested blot, and then be able to get the deltas using the getContents.

For example, how do I even go about coming up with this simple nesting for iframe using blot:

<div style="left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.2493%;">
     <iframe src="https://www.youtube.com/embed/6LuREkiF_Hs?rel=0&amp;showinfo=0" style="border: 0; top: 0; left: 0; width: 100%; height: 100%; position: absolute;" allowfullscreen scrolling="no">
     </iframe>
</div>

@barry800414
Copy link

barry800414 commented Nov 1, 2017

I have the same requirement as @kilgarenone

I want to insert something like this, a div wrapper to wrap iframe element to control the size of iframe. However, I have tried my best, I still cannot make it.

    <div class="video-container">
        <iframe src="xxx">
        </iframe>
    </div>

Thie followings are my code (using react-quill) :

const BlockEmbed = Quill.import('blots/block/embed');
class VideoContainer extends BlockEmbed {
  static create(value) {
    const node = super.create();
    node.setAttribute('class', 'video-container');

    const child = document.createElement('iframe');
    child.setAttribute('src', value);
    child.setAttribute('frameborder', 0);
    child.setAttribute('allowfullscreen', true);
    node.appendChild(child);
    return node;
  }

  // get value of the node (for implement undo function)
  static value(node) {
    return node.firstChild.getAttribute('src');
  }
}

VideoContainer.blotName = 'video-container';
VideoContainer.tagName = 'div';
Quill.register(VideoContainer);

function insertVimeo() {
  const vimRegExp = /\/\/(player.)?vimeo.com\/([a-z]*\/)*([0-9]{6,11})[?]?.*/;
  const url = prompt('Enter Vimeo URL:');
  if (url && vimRegExp.test(url)) {
    // generate embed link
    const vimMatch = url.match(vimRegExp);
    const src = ['//player.vimeo.com/video/', vimMatch[3]].join('');

    const cursorPosition = this.quill.getSelection().index;
    this.quill.insertEmbed(cursorPosition, 'video-container', src);
    this.quill.setSelection(cursorPosition + 1);
  }
}

class QuillScreen extends Component {
  constructor(props) {
    super(props);
    this.state = { text: '' };
    this.handleChange = this.handleChange.bind(this);

    this.toolbarOptions = {
      // suppose to be all supported toolbar buttons
      container: [
        // sample for adding new button in toolbar
        ['vimeo'],
        ['bold', 'italic', 'underline', 'strike'], // toggled buttons
        ['blockquote', 'code-block', 'code', 'formula', 'link'],
        ['video', 'image'],

        [{ 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
      ],
      handlers: {
        vimeo: insertVimeo,
      },
    };

    // suppose to be all supported format
    this.formats = [
      ['background', 'bold', 'color', 'font', 'code', 'italic', 'link', 'size', 'strike', 'script', 'underline'],
      ['blockquote', 'header', 'indent', 'list', 'align', 'direction', 'code-block'],
      ['formula', 'image', 'video'],
      ['video-container'],
    ];
  }

  handleChange(value) {
    this.setState({ text: value });
  }

  render() {
    return (
      <ReactQuill
        value={this.state.text}
        onChange={this.handleChange}
        modules={{
          toolbar: this.toolbarOptions,
        }}
      />
    );
  }
}

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

No branches or pull requests

5 participants