RTE Text Alignment findings & assistance

default discord avatar
Mark | Omniux
4 months ago
5

Hey folks, I've been trying to build text alignment into my RTE (like I'm sure a lot of you have tried to do as well) and I'm wanting to share some of the stuff I've done so far. Just a disclaimer, this is currently working in a very limited capacity, the UI is glitchy, and the only thing is achieves so far is mutating the target node to have alignment as an option. I have trawled through every CMS project on the Payload Github to identify key areas for developing a custom text alignment element. Here are my findings so far;



- Text Alignment should be done using an Element rather than a Leaf, this is because leafs alter their child elements directly, whereas Elements affect a block. So if you have a h4 tag with Bold and Underline leaves, you want to add alignment to the entire text block rather than the normal text, bold and underline leaves.


- Text Alignment should not be treated as a unique element, it is an extension of the current node. According to this post -

https://news.ycombinator.com/item?id=14127632

- the author of Slate states that you should add custom data to the nodes, rather than setting the 'type' property of the node (like what happens currently when applying h1, h2, h3...)


- When attempting to use Slatejs or Slate-react directly inside Element or Button code, YOU DO NOT NEED TO NPM INSTALL THESE PACKAGES. Instead, using the version used by payload. These packages can be accessed from

payload/node_modules/slate

and

payload/node_modules/slate-react


And now, my code;



import React, { useCallback } from 'react';
import { ElementButton } from 'payload/components/rich-text';
import { useSlate, ReactEditor } from 'payload/node_modules/slate-react'
import { Transforms, Editor, Element as SlateElement } from 'payload/node_modules/slate'
import { IconAlignCenter, IconAlignLeft, IconAlignRight } from '../Icons';

const TEXT_ALIGN_TYPES = ['left', 'center', 'right']

type AlignmentNode = Partial<SlateElement &
{
    align?: typeof TEXT_ALIGN_TYPES,
    type: string
}>

const addAlignment = (editor, format) => {

    let targetNode: AlignmentNode = undefined;

    Transforms.unwrapNodes(editor, {
        match: n => {
            var match = !Editor.isEditor(n) &&
                SlateElement.isElement(n) &&
                !TEXT_ALIGN_TYPES.includes(format)

            if (match) {
                targetNode = n as AlignmentNode;
                return true;
            }
            return false;
        },
        split: true,
    })

    let newProperties: AlignmentNode = targetNode

    newProperties = {
        align: format,
    }

    Transforms.setNodes<SlateElement>(editor, newProperties)

    ReactEditor.focus(editor);
};

const Button: React.FC<{ path: string }> = () => {

    const editor = useSlate();

    return (<>
        <ElementButton onClick={useCallback(() => addAlignment(editor, 'left'), [editor])}
            tooltip='left'
            format='left'
        >
            <IconAlignLeft />
        </ElementButton>

        <ElementButton onClick={useCallback(() => addAlignment(editor, 'center'), [editor])}
            tooltip='center'
            format='center'
        >
            <IconAlignCenter />
        </ElementButton>

        <ElementButton onClick={useCallback(() => addAlignment(editor, 'right'), [editor])}
            tooltip='right'
            format='right'
        >
            <IconAlignRight />
        </ElementButton>
    </>
    );
}

export default Button


- Does this work?



Yeeeeaahhmmhmm... It works in such a way that it adds a custom node to the top level element. It adds an alignment field when it's applied; either left, right, or center. It doesn't actually change anything in the Editor UI as for some reason the element re-render doesn't seem to get triggered. Because it doesn't re-trigger, I am unable to actually make changes to way the text is displayed on the editor. HOWEVER, rest assured that the change has actually happened, so json object passed to your website front-end WILL show this change (big win). Obviously this isn't workable from a general user perspective as the UI appears to be unresponsive in Payload. But it's a start!



So guys, I have a baseline. Who wants to help improve it so we can actually show this in the Payload UI?

  • discord user avatar
    zubricks
    Payload Team
    3 months ago

    Hey Mark - this is certainly something that should be on our roadmap if it isn't already. I'll get some more answers on this one for you shortly. Stay tuned!

  • default discord avatar
    Mark | Omniux
    3 months ago

    No worries @zubricks 🙂 I saw that 1.6 released with a new toggleElement function, that might be the thing that solves the button toggle issue. If that's the case I'll share the updated code

  • default discord avatar
    sjj1991
    last week

    hi @Mark | Omniux , did you managed to work on the updated code with the toggleElement function?

Open the post
Continue the discussion in Discord
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.