facebookarchive / draft-js

A React framework for building text editors.
https://draftjs.org/
MIT License
22.58k stars 2.64k forks source link

getCurrentInlineStyle behaves inconsistently #722

Open smithjustinb opened 8 years ago

smithjustinb commented 8 years ago

I believe there is a bug in getCurrentInlineStyle (or, if it's current behavior is intentional, it is inconsistent with typical rich text editor behavior).

When selecting a block of text in which different characters have different styles, getCurrentInlineStyle usually doesn't show styles that aren't applied to the entire selection. This seems to be standard practice. However, it will show styles that are applied to the very start of the selection, even if they aren't applied throughout, which seems inconsistent.

This can be observed in the sample editor at https://facebook.github.io/draft-js/ Enter : foo bar bat. Highlight the whole line and the bold button will not be lit. Highlight "foo bar" and, again, bold is unlit. Highlight "bar bat" and bold will light, despite the fact that all the characters in the selection are not bold.

I'm seeing this in version 0.9.1

davidchang commented 8 years ago

actually, eek - i mislabeled this. getCurrentInlineStyle, by definition, when you have selected multiple characters, doesn't compute the intersection of all of those characters' styles. it just uses that of the first character, which is why highlighting "bar bat" takes the inline style of the "b" in "bar", which happens to be bold.

i'd say this is a bug with the demo that needs to be fixed, but not a bug with the API.

this is also the reason why the draftjs-utils project by @jpuri implements a getSelectionInlineStyle method: https://github.com/jpuri/draftjs-utils/blob/master/js/inline.js#L24-L59

davidchang commented 8 years ago

@hellendag - could you give some context as to why getCurrentInlineStyle doesn't take the intersection?

i was going to patch the rich text example with an adaptation of getSelectionInlineStyle (link above)

asiraky commented 2 years ago

I know this is an old issue but I will post my solution here for others to see. I took code from draftjs-utils and modified it to be more generic. I found the draftjs-util implementation was geared towards the built in styles e.g. BOLD ITALIC etc. It didnt account for custom inline styles.

const getSelectionInlineStyle = (editorState: EditorState) => {
    const currentSelection = editorState.getSelection()

    if (currentSelection.isCollapsed()) {
        return editorState.getCurrentInlineStyle()
    }

    const start = currentSelection.getStartOffset()
    const end = currentSelection.getEndOffset()
    const selectedBlocks = getSelectedBlocksMap(editorState).toList()

    let set = Immutable.OrderedSet<string>()

    if (selectedBlocks.size > 0) {
        for (let i = 0; i < selectedBlocks.size; i += 1) {
            let blockStart = i === 0 ? start : 0;
            let blockEnd =
                i === selectedBlocks.size - 1
                    ? end
                    : selectedBlocks.get(i).getText().length
            if (blockStart === blockEnd && blockStart === 0) {
                blockStart = 1
                blockEnd = 2
            } else if (blockStart === blockEnd) {
                blockStart -= 1
            }
            for (let j = blockStart; j < blockEnd; j += 1) {
                set = set.merge(selectedBlocks.get(i).getInlineStyleAt(j))
            }
        }
    }

    return set
}

const getSelectedBlocksMap = (editorState: EditorState) => {
    const selectionState = editorState.getSelection()
    const contentState = editorState.getCurrentContent()
    const startKey = selectionState.getStartKey()
    const endKey = selectionState.getEndKey()
    const blockMap = contentState.getBlockMap()
    return blockMap
        .toSeq()
        .skipUntil((_, k) => k === startKey)
        .takeUntil((_, k) => k === endKey)
        .concat([[endKey, blockMap.get(endKey)]])
}

Calling getSelectionInlineStyle(myState) will now return a set of intersecting styles, taking into account multiple blocks.