Draft-js: Get selection as text

Created on 5 Jun 2016  路  11Comments  路  Source: facebook/draft-js

I was not able to find any method like getTextSelection(contentState: ContentState, selection: SelectionState, blockDelimiter: String) -> String.

Is there one that I missed?

question

Most helpful comment

This is a case where getContentStateFragment would be useful if exposed.

const fragment = getContentStateFragment(contentState, selectionState);
const plainText = fragment.map((block) => block.getText()).join(delimiter);

I think I'm fine with exposing it -- there's a PR to expose pretty much all of the transaction modules anyway.

All 11 comments

Maybe it's early and I'm not fully awake yet, but what is your use case? (what is blockDelimiter for?)

My use case is to get the selected text in the editor to write it in a custom clipboard (precise use case is hard to describe).

Here is a basic (non tested) implementation that I come up with, but I was wondering if Draft already includes something like this since it has ContentState.getPlainText:

/**
 * Get current selected text
 * @param  {Draft.ContentState}
 * @param  {Draft.SelectionState}
 * @param  {String}
 * @return {String}
 */
function _getTextSelection(contentState, selection, blockDelimiter) {
    blockDelimiter = blockDelimiter || '\n';
    var startKey   = selection.getStartKey();
    var endKey     = selection.getEndKey();
    var blocks     = contentState.getBlockMap();

    var lastWasEnd = false;
    var selectedBlock = blocks
        .skipUntil(function(block) {
            return block.getKey() === startKey;
        })
        .takeUntil(function(block) {
            var result = lastWasEnd;

            if (block.getKey() === endKey) {
                lastWasEnd = true;
            }

            return result;
        });

    return selectedBlock
        .map(function(block) {
            var key = block.getKey();
            var text = block.getText();

            var start = 0;
            var end = text.length;

            if (key === startKey) {
                start = selection.getStartOffset();
            }
            if (key === endKey) {
                end = selection.getEndOffset();
            }

            text = text.slice(start, end);
            return text;
        })
        .join(blockDelimiter);
}

Nope, that's pretty much a good way to do it. :)

This is a case where getContentStateFragment would be useful if exposed.

const fragment = getContentStateFragment(contentState, selectionState);
const plainText = fragment.map((block) => block.getText()).join(delimiter);

I think I'm fine with exposing it -- there's a PR to expose pretty much all of the transaction modules anyway.

@hellendag Any idea when the PR is getting merged ?

@SamyPesse I was able to piece together something like the following -

// Get block for current selection
let selection = editorState.getSelection();
const anchorKey = selection.getAnchorKey();
const currentContent = editorState.getCurrentContent();
const currentBlock = currentContent.getBlockForKey(anchorKey);

//Then based on the docs for SelectionState -
const start = selection.getStartOffset();
const end = selection.getEndOffset();
const selectedText = currentBlock.getText().slice(start, end);

It works, but I'm looking to forward to hearing why/if this is not the best the way to do it. @hellendag @davidbyttow

@robinmaben Your solution assumes the selection doesn't span more than a single block. If it does, you need to grab all the selected blocks, as SamyPesse is doing in his comment.

@hellendag What's the status of exposing getContentStateFragment, please?

window.getSelection().toString()

...
import getFragmentFromSelection from 'draft-js/lib/getFragmentFromSelection';
...

const selected = getFragmentFromSelection(this.state.editorState);
console.log(selected ? selected.map(x => x.getText()).join('\n') : '')

@hellendag any update on exposing getContentStateFragment?

Was this page helpful?
0 / 5 - 0 ratings