Search code examples
reactjsdraftjsdraft-js-plugins

DraftJs trim content with all styling and other properties, not just text


I have a react application and using DraftJs.

On listing pages my ContentCard component renders DraftJs content as read-only using Editor from draft-js.

 <Editor readOnly={true} editorState={contentState} /> 

I want to show short version of contentState as short description, max 400 characters at listing pages. And on content detail pages full contentState.

I used this truncate method but it trims only text. Here i get block and then texts. But how can i get blocks with character limit.

For example; First block contains 820 characters with all different styling words. How can i get first 400 characters with all styling information. I mean block with 400 character.

truncate = (editorState, charCount) => {

    const contentState = editorState.getCurrentContent();
    const blocks = contentState.getBlocksAsArray();

    let index = 0;
    let currentLength = 0;
    let isTruncated = false;
    const truncatedBlocks = [];

    while (!isTruncated && blocks[index]) {
        const block = blocks[index];
        const length = block.getLength();
        if (currentLength + length > charCount) {
            isTruncated = true;
            const truncatedText = block
                .getText()
                .slice(0, charCount - currentLength);
            const state = ContentState.createFromText(`${truncatedText}...`);
            truncatedBlocks.push(state.getFirstBlock());
        } else {
            truncatedBlocks.push(block);
        }
        currentLength += length + 1;
        index++;
    }

    if (isTruncated) {
        const state = ContentState.createFromBlockArray(truncatedBlocks);
        return EditorState.createWithContent(state);
    }

    return editorState;
};

I want to show 400 characters with bold, italic styling, links and opher entities and so on.


Solution

  • I needed something similar, so I did a very crude implementation, but it worked for me :)

    import { ContentState, convertToRaw } from 'draft-js'
    
    const convertContentToEditorState = (content: string) => {
        if (content) {
            try {
                return EditorState.createWithContent(
                    convertFromRaw(JSON.parse(content)),
                )
            } catch {
                return EditorState.createEmpty()
            }
        } else {
            return EditorState.createEmpty()
        }
    }
    
    /**
     * Takes in a stringfied JSON object, truncates it into a single ContentState and returns it.
     * @param jsonContentBlocks
     * @param maxCharCount
     */
    const getTruncatedContentState = (
        jsonContentBlocks: string,
        maxCharCount: number,
    ): ContentState | undefined => {
        const editorState = convertContentToEditorState(jsonContentBlocks)
    
        const contentState = editorState.getCurrentContent()
        const blocks = contentState.getBlocksAsArray()
    
        let currentLength = 0
        const truncatedBlocks = []
    
        for (let i = 0; i < blocks.length; i++) {
            const blockLength = blocks[i].getCharacterList().size
            let truncatedText = ''
    
            if (blockLength >= maxCharCount - currentLength) {
                // We need to trim it
                truncatedText = blocks[i]
                    .getText()
                    .slice(0, maxCharCount - currentLength)
                currentLength += truncatedText.length
    
                const state = ContentState.createFromText(`${truncatedText}...`)
                truncatedBlocks.push(state.getFirstBlock())
                break
            } else if (blockLength > 0) {
                truncatedText = blocks[i].getText()
                currentLength += truncatedText.length
    
                const state = ContentState.createFromText(`${truncatedText}`)
                truncatedBlocks.push(state.getFirstBlock())
            }
        }
    
        if (truncatedBlocks.length > 0) {
            return ContentState.createFromBlockArray(truncatedBlocks)
        }
    
        return undefined
    }
    
    /**
     * Truncates and gets only the text from the blocks, returns stringified JSON
     * @param jsonContentBlocks
     * @param maxCharCount
     */
    const getTruncatedContent = (
        jsonContentBlocks: string | undefined,
        maxCharCount: number,
    ): string | undefined => {
        if (!jsonContentBlocks) return undefined
    
        const contentState = getTruncatedContentState(
            jsonContentBlocks,
            maxCharCount,
        )
    
        if (contentState) {
            const raw = convertToRaw(contentState)
            return JSON.stringify(raw)
        }
    
        return undefined
    }