#Has anyone added Text Color to the RichText input?
241 messages · Page 1 of 1 (latest)
You mean like a color-picking leaf?
I think so! We want to add text color to the richText editor
I've done a simple leaf that just applies an attribute to the selected text.. might be a good starting place
import { LeafButton } from 'payload/components/rich-text'
import './index.scss'
const baseClass = 'custom-leaf'
const name = 'custom_leaf'
const Button = () => (
<LeafButton format={name}>
Custom Button (You might want to pop out the color picker here)
</LeafButton>
)
const Leaf = ({ attributes, leaf, children }) => {
if (leaf[name]) {
return <span {...attributes} className={baseClass}>{children}</span>
}
return <span {...attributes}>{children}</span>
}
export default {
name,
Button,
Leaf,
}```
from there you could probably hook into a color picker component to add a style prop instead of a className to do a direct adjustment of the color value
just hypothetical of course :). I had a hard time making just a custom leaf in the first place so maybe this'll get you closer.
Thanks @tired loom 😄
@tired loom I'm bad at react so this was a challenge
Well it is a challenge, but I'm getting closer?
import React from 'react';
import { LeafButton } from 'payload/components/rich-text'
let spanStyle = {
color: '#000000'
}
const onChange = (event) => {
spanStyle.color = event.target.value
}
const Color = ({ attributes, children }) => (
<span style={spanStyle} {...attributes}>{children}</span>
);
const color = {
Button: () => (
<LeafButton format="color">
<input onChange={onChange} type="color"></input>
</LeafButton>
),
Leaf: Color,
};
export default color;
not sure why the style isn't applied
your color export doesn't appear to have a name property. I think that's necessary for one
ooooo
you might also want to include the if (leaf[name]) { stuff.. I never verified if that's necessary though.
ahhh
also, I didn't know there was a 'color' input type till today, lol. TIL
I think your onChange event makes sense, but where you're probably also losing the logical flow is when you need that spanStyle to update when it changes
but.. maybe that's where the leaf property comes in
when it changes, the output adjusts to when a leaf is applied vs without in my example. so maybe that's what's missing for you as well
hmmmm
so currently I have
import React from 'react';
import { LeafButton } from 'payload/components/rich-text'
const baseClass = 'custom-leaf'
const name = 'color'
let spanStyle = {
color: '#000000'
}
const onChange = (event) => {
spanStyle.color = event.target.value
}
const Color = ({ attributes, leaf, children }) => {
if (leaf[name]) {
return <span style={spanStyle} {...attributes} className={baseClass}>{children}</span>
}
return <span style={spanStyle} {...attributes}>{children}</span>
}
const color = {
Button: () => (
<LeafButton format="color">
<input onChange={onChange} type="color"></input>
</LeafButton>
),
Leaf: Color,
};
export default color;
You're saying I should do something with the leaf prop?
yeah, I think what you have there looks right
you might not need the className
but in the default export, you'll want a property of name: 'color'
you might also remove the style attribute on the non-leaf condition
I think I already added that no?
Not sure how to export name: 'color' from the color obj
name: 'color',
Button: () => (
<LeafButton format="color">
<input onChange={onChange} type="color"></input>
</LeafButton>
),
Leaf: Color,
};```
like that
all good lol
Hmmm
So it highlights the button when i hover over text i tried to apply color to
which is good
but no luck yet getting the style applied
nice nice, progress!
@sacred pasture I saw you did a markdown plugin recently, maybe you can help identify why the span doesn't receive any style?
@vestal jungle If you get a moment today, I'm super confused on how to implement that color input, I think it's pretty close.
I wish I knew React
maybe it's time to bit the bullet
Lemme try it out myself for a couple minutes here
these types of threads make me happy
it is very cool to see the collaboration happening!
import { LeafButton } from 'payload/components/rich-text'
const name = 'color_picker'
let color = '#000000'
const onChange = (event) => {
console.log('color value changed', event.target.value)
color = event.target.value
}
const Button = () => (
<LeafButton format={name}>
<input onChange={onChange} type="color"></input>
</LeafButton>
)
const Leaf = ({ attributes, leaf, children }) => {
if (leaf[name]) {
return <span style={{color}} {...attributes}>{children}</span>
}
return <span {...attributes}>{children}</span>
}
export default {
name,
Button,
Leaf,
}```
this gets us closer, but the updating of the color is lagged
like if you set the color, then click around a bit, it eventually updates
maybe there's something in the docs that helps explain..
seems to update the state of the text when you click the leaf again
hm, the fundamental issue here appears to be that leaves can only carry around a boolean value
i.e. whether or not selected text has a given style applied or not
so even if we got it to work in the editor, it's only going to provide true/false anyway
at least with how we currently have it
might be able to get it working with this though.. https://www.npmjs.com/package/@slate-editor/color-plugin
I'll try it real quick here... the I gotta change gears for the time being
Thank you @tired loom
Right that was my first though
thought
However, just importing that plugin into the collection file
And adding it to the plugins array in my leaf config, caused an error
import { LeafButton } from 'payload/components/rich-text'
import { ColorPlugin, ColorButton, ColorStateModel, ColorMark } from '@slate-editor/color-plugin'
const name = 'color_picker'
const colorPluginOptions = new ColorStateModel().rgba({ r: 100, g: 100, b: 100, a: 1 }).gen()
const Button = () => {
return (
<ColorButton format={name}>
initialState={colorPluginOptions}
pickerDefaultPosition={{ x: -520, y: 17 }}
</ColorButton>
)
}
const Leaf = ({ attributes, leaf, children }) => {
if (leaf[name]) {
console.log(leaf)
return <span {...attributes}>{children}</span>
}
return <span {...attributes}>{children}</span>
}
const plugins = [
ColorPlugin()
]
export default {
name,
Button,
Leaf,
plugins,
}```
the main thing I haven't got working here is how to get the "leaf" portion working
my guess is it has to do with the "ColorMark" from the color-plugin since its code resembles that of a leaf/element
Hmm... yeah I gotta change gears. But part of the issue atm is not knowing how to get the plugin to work through payload's passthru to slate.js
hmmm
the color picker example doesn't show a definition of a leaf or element. i'm guessing because a 'ColorMark' is used instead through the plugin definition
but if you don't supply a leaf or element, it seems payload complains
ok... last try this time lol ```import React from 'react'
import { LeafButton } from 'payload/components/rich-text'
import { ColorPlugin, ColorButton, ColorStateModel, ColorMark } from '@slate-editor/color-plugin'
const name = 'color_picker'
const colorPluginOptions = new ColorStateModel().rgba({ r: 100, g: 100, b: 100, a: 1 }).gen()
const Button = () => {
return (
<ColorButton format={name}>
initialState={colorPluginOptions}
pickerDefaultPosition={{ x: -520, y: 17 }}
</ColorButton>
)
}
const Leaf = ({ attributes, leaf, children }) => {
if (leaf[name]) {
return <ColorMark {...attributes}>{children}</ColorMark>
}
return <span {...attributes}>{children}</span>
}
const plugins = [
ColorPlugin()
]
export default {
name,
Button,
Leaf,
plugins,
}
I noticed ColorMark returns a react element so.. this might be closer
but it errors due to an undefined object
makes me wonder if this plugin is just too old and out of date with slate perhaps
might want to just start up a clean project with just slate.js and try to use this plugin in it
could reveal some things we need to do in the payload context
good luck for now!
Will do TY so much!
Got the color applied
But still bad at React
lmao
@tired loom progress
import React from "react";
import { LeafButton } from "payload/components/rich-text";
import {Editor} from 'slate'
import { useSlate } from 'slate-react';
const name = "color_picker";
let color = "#000000";
const isLeafActive = (editor) => {
const leaves = Editor.marks(editor);
return leaves ? leaves[name] === true : false;
};
const onChange = (event) => {
color = event.target.value;
};
const Button = ({format, children}) => {
const editor = useSlate()
const active = isLeafActive(editor)
if (active) {
// Set input color to leaf color somehow
} else {
// Set input color to default somehow
}
return <div><input onChange={onChange} type="color"></input><LeafButton format={name} >
{active ? 'Remove Color' : 'Apply Color'}
</LeafButton></div>
};
const Leaf = ({ attributes, leaf, children }) => {
if (leaf[name]) {
return (
<span style={{
color
}} {...attributes}>
{children}
</span>
);
}
return <span {...attributes}>{children}</span>;
};
export default {
name,
Button,
Leaf,
};
So it applies the color, and recognizes when the leaf is active
But not sure how to remove the style if the button is pressed while it is active
Also not sure how to set the input to the leaf color when leaf is active
can you confirm that the data of the color you set is coming out in the api too? i.e. if you request this data, will the color be there to use
Hmmm
Working on that
tried doing like
leaf[name] = color
and also leaf.color
in case it allowed other props
neither save that on the item
only color_picker=true
Yeah, that detail is what eludes me. My guess is the color picker plugin might reveal how that is done if you look at the source
:*(
HELLO
i am alive
trying to clarify a few things quick
- the color picker plugin is a custom field, right? so there should likely be little overlap besides the actual JS to open a color picker dialog
attributesin Slate are not where you'd want to store a color. That is an internal Slate convention. Instead, you store the "state" of a Slate leaf / element directly on the node itself as additional properties
the best way to build this IMO is to look at a simple custom leaf, like here:
there are only a few small things that need to be modified from this example:
- the
Buttoncomponent should be modified to allow the user to pick a color, and then from there, apply it to the node, rather than simply toggling the property on or off usingtoggleLeaf. So instead of re-using the built-inLeafButton, you need to make your own, and customize the logic that is contained within there - Instead of just storing a boolean
true/falseon the leaf itself, you would store the hex value of the color that was selected by the user
then boom done
haven't lost sight of this btw. I'm going to give it a try as well when I finish my work here
Woot!
yeah I didn't make more progress with the example provided
It's still not clear how we save the color on the leaf object
@vestal jungle I think I'm most confused on the you would store the hex value of the color that was selected by the user
I did try to set the value of color_picker (name) to the hex
But it did not seem to save it
here is a basic rich text document with 2 "leaves" - the first one has no "marks", but the second one has a bold "mark"
notice that the actual "state" of bold is stored directly on the leaf as a boolean
rather than storing a boolean, you would store your hex value of the color that the user chose directly on the leaf next to bold
I think I did try that, like this?
const Leaf = ({ attributes, leaf, children }) => {
if (leaf[name]) {
leaf[name] = color
return (
<span style={{
color
}} {...attributes}>
{children}
</span>
);
}
return <span {...attributes}>{children}</span>;
};
well, you still need that part, but that is JUST regarding how to render the leaf in the rich text editor itself. you still need to actually store the value on the leaf
hmm
your actual leaf component itself looks good
but are you actually storing the prop on the leaf? this would be done in the button, not the leaf component itself
I don't think I am, that's where I'm struggling
Totally thought it was in the leaf logic
nope. think of the leaf logic as just simply a way to render the output
note that you CAN allow them to re-choose the color from the leaf itself
but the first step is actually just using the button to "toggle on" the color
then from there, if you wanted to, you could use the leaf component to allow the color to be changed or removed
but that is optional
Are there examples of a button saving data?
I'm not sure at which part, maybe I should check out that recent markdown example?
yes, that PurpleBackground example above shows that
you click the button and it enables the purple background on the leaf
but it only stores a boolean. you need to store the hex value
Oooo ok, I think I'm confused on which part is responsible for saving, because
const Button = () => (
<LeafButton format="purple-background">
Purple Background
</LeafButton>
);
Format seems to set that object prop right?
- the Button component should be modified to allow the user to pick a color, and then from there, apply it to the node, rather than simply toggling the property on or off using toggleLeaf. So instead of re-using the built-in LeafButton, you need to make your own, and customize the logic that is contained within there
the logic to save / toggle the prop on the leaf is contained within the LeafButton component
you need to not use that, and write your own version of that
Ahhhhh
because that component is simple and just toggles a boolean on / off
Ok that's the part I didn't get, I'll review that component
so it's not applicable for your re-use
@tropic wolf ever get it cleaned up? I'm curious to see it!
AYY
I was planning on cleaning it up more today, but this is what I currently have
So the current issues are
1.) Change the color from the input, select a string, press the apply color button. The text will change color. If you select a different node, and click on the colored node, the button will recognize the leaf, but will not remove the formatting unless the whole string is selected again
2.) Click on a leaf should update the state of the color input, I commented out that part as I was stuck on it
3.) General styling
@tired loom Let me know your thoughts
I think this great progress! I'll have to give it a try myself at some point here. Great work!
Thanks to everyone for their help!
Hi there, I was looking for something that was capable of doing the same as this gist. I have just tried what is done here but is giving me error related to React children and can't find how to solve. Any help?
Hey hey! Could you share the error and optimally the code as well?
Hey @hasty jacinth the code is the same as in the gist and here is how it's being used
Then on the UI there's this
And as soon as I type this happens
What do you think?
@charred kernel were you following the code in my gist?
Hi @tropic wolf, yes! I just went there and copy, sorry about that
No problem
Here is how to implement it in a collection config
import { Block } from "payload/types";
import TestLeaf from "../../editor/TestLeaf";
const SupportModalBlock: Block = {
slug: "SupportModal",
fields: [
{
name: "modalTitle",
label: "Support Modal Title",
type: "text",
required: true,
},
{
name: "modalContent",
label: "Support Modal Content",
type: "richText",
admin: {
leaves: [
{
name: "color_picker",
Button: TestLeaf.Button,
Leaf: TestLeaf.Leaf,
plugins: [
// any plugins that are required by this leaf go here
],
},
],
},
},
{
name: "modalPhone",
label: "Support Phone",
type: "text",
required: true,
},
{
name: "modalEmail",
label: "Support Email",
type: "text",
required: true,
},
{
name: "modalFax",
label: "Support Fax",
type: "text",
required: true,
},
],
};
export { SupportModalBlock };
Let me know if that works
Also note: This picker is a WIP, if you make it better please post here
I'm having a look at this lines. It seem that the error comes from here
It
It's interesting you're getting an error
I think I have the same code without issue
let me check my latest
thats my current
@tropic wolf now I can say that the error is not from your code at all. It seems to be related with the richText field itself since I'm getting the same error even when using it as its simplest implementation.
Thank you very much for the help!
Hi Guys, i use your solution @tropic wolf and get Error: Uncaught Error: The useSlate hook must be used inside the <Slate> component's context.
what am I doing wrong?
@tropic wolf could you show your package.json file your Payload CMS project ?
Sure
@shadow lion Can i check out your collection config?
The package.json shouldn't matter at all since we are not adding any new dependencies
and payload is at latest
WOW I was only able to solve this problem because it needed to install a version dependency: "slate": "0.88.1",
"slate-react": "0.88.0",
yeah exactly
@tropic wolf sorry for the question, but how to get the attribute color on the consumer (client app) ?
I only get the text in the node.text ... but I don’t understand how to get the color attribute for displaying it ..
@shadow lion it should be saving the color to the lead in the generated json
@tropic wolf I got this error ( Error: Uncaught Error: The useSlate hook must be used inside the <Slate> component's context. ) again after a while, maybe the version of 'react-slate' that Payload uses has changed?
@shadow lion Possible, I'm actually not super familiar with the inner workings of Slate and my initial implementation was a learning experiment. @hasty jacinth may have some insight though!
Hey @tropic wolf , does this custom leaf still work? I used the code from your gist but it doesn't update the color in the richtext and payload also doesn't recognize that it made a change
Ah now it works, I didn't know that you have to select the color before you select your text but there is another issue: if I select my text that has colors the color picker pick the color from the text
@tropic wolf Hi, I am getting the following error Object literal may only specify known properties, and 'leaves' does not exist in type 'Admin & { components?: { Error?: ComponentType<Props>; Label?: ComponentType<Props>; }; }
I've created a plugin to edit text color, highlight and block background, maybe you can check if it cover your needs https://www.npmjs.com/package/payloadcms-lexical-ext
hey. while using this plugin getting this error : Node HeadingNode has not been registered. Ensure node has been passed to createEditor.
why?
Any updates on this?
https://github.com/rubn-g/payloadcms-lexical-ext/issues/8
Added my fix to the comment @amber quest , would like some comments re this
Any solution to add color to richtext?
plenty of options now. you can use my plugin:
https://github.com/AdrianMaj/payload-lexical-typography
or new TextStateFeature from the core of Payload
Facing a weird issue with the select:
yeah i know it’s there but no time to fix it, you have to use fixed toolbar
not the inline one
you can contribute if you want to
No prob, would love to contribute 😄
If you're looking for a walkthrough on how to use the core TextStateFeature, here you go: https://youtu.be/RHBdlP7a-sA
Repo — https://github.com/nlvcodes/nlv_codes_example/tree/20250701-textstatefeature-implementation
Become a paying member of the channel — https://www.youtube.com/channel/UC8mOjhMBTyPPScCF9vYtwFg/join
Find me on Twitch — https://www.twitch.tv/nlv_codes
Want to stay up to date with recent web dev news? Subscribe to my newsletter — https:...
I just used that for a great feature: activating a serif font. Why is this feature not documented ?? 😄 why do I first need to struggle before finding your video?
glad you figured it out!