Wrap selection with node

TL;DR - way to get content from selection is?

I tried few examples you posted around, but I can’t workout how to make command that would wrap selected text by my node/widget.

I create something like twitterable quote. I wrap selected content with

<a href="..."><blockquote><p>(content)</p></blockqoute></a>

It works nicely when I render it from data

Twitterable.prototype.serializeDOM = (node, serializer, c, d, e, f, g) => {
    let innerContent = '',
        innerContentClear = '',
        attributes = {
            class: 'twitterable'
        };
    if (node.rendered) {
        node.rendered = node.rendered.cloneNode(true)
    } else {
        try {
            if (node && node.content && node.content[0] && node.content[0].type) {
                innerContent = serializer.renderNode(node.content.content[0]);
                innerContentClear = toText(node.content.content[0]);
            }
        } catch (e) {
            console.error(e);
        }
        if (serializer.options.serverSide === true) {
            attributes.href = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(innerContentClear + ' ' + serializer.options.uri);
        }
        node.rendered = elt('a', attributes, [
            elt('blockquote', {}, [
                elt('p', {},
                    innerContent)
        ])]);
    }
    return node.rendered;
};

But I can’t work out how to enable selecting fragment and wrapping it. I found that it may be good to use code like here:

Twitterable.register('command', 'insert', {
    run(pm) {
        return pm.tr.replaceSelection(this.create({}));
      },
})

Anyhow basing on doc and examples I can’t workout how to:

  • get reference to selected fragment
  • what is correct way of passing it to new node (in worst scenario I could do node.content.content.push(selectedNode)

Any ideas or reference?

(As an aside, don’t render the inner content with renderNode. Use renderAs instead.)

Is this a block node type? Are you trying to wrap the whole textblock, or only the actually selected text? You can get the content of a selection by calling slice on the document and passing in the from and to from the selection. Is that what you were looking for?

1 Like

In ideal case that would be block. As it create a > blockquote > p

run(pm) {
        let content = pm.doc.slice(pm.selection.from, pm.selection.to);
        console.log(content);
        return pm.tr.replaceSelection(this.create({}));
      },

content is Node of type doc and nothing in content, even, if I select text.

Sorry, it’s not obvious (what doesn’t mean it’s wrong).

Sorry, my bad, I meant sliceBetween. But that’ll still yet you a Doc node, since selections may have an arbitrary shape and can not necessarily be represented as a single node or flat list of nodes.

If you only want to deal with selection within a single textblock, you can do something like

let parent = pm.doc.path(from.path)
if (Pos.samePath(from.path, to.path) && parent.isTextblock) {
   // Fragment object containing slice of inline nodes
  let content = parent.slice(from.offset, to.offset)
  pm.tr.replaceSelection(MY_NODE_TYPE.create(null, content)).apply()
}
1 Like

Thanks, following code works perfectly

Twitterable.prototype.serializeDOM = (node, serializer) => {

    let innerContent = '',
        innerContentClear = '',
        attributes = {
            class: 'twitterable'
        };

    if (node.rendered) {
        node.rendered = node.rendered.cloneNode(true)
    } else {
        try {
            if (node && node.content && node.content.content && node.content.content[0].type) {
                innerContent = serializer.renderAs(node.content.content[0], 'p');
                innerContentClear = toText(node.content.content[0]);
            }
        } catch (e) {
            console.error(e);
        }
        if (serializer.options.serverSide === true) {
            attributes.href = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(innerContentClear + ' ' + serializer.options.uri);
        }

        node.rendered = elt('a', attributes, [elt('blockquote', {}, innerContent)]);

    }
    return node.rendered;
};

Twitterable.register('command', 'insert', {
    label: 'Wrap the selection in a block quote',
    run(pm) {
        let content = pm.doc.sliceBetween(pm.selection.from, pm.selection.to),
            node = this.create(null, content.content);
        return pm.tr.replaceSelection(node).apply();
    },
    menuGroup: 'block(46)',
})