Wondering if anyone has worked with slatejs and getting some of the features like text color added to the rich text component? Having a hard time understanding the setup as I'm not really a React guy.
https://www.npmjs.com/package/@slate-editor/color-plugin
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 React from 'react'
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 @Jakey π
@Jakey 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
like..
export default {
color,
name: 'color'
}
?
nah, in your original color object definition there
above Button, probably
I think I already added that no?
Not sure how to export name: 'color' from the color obj
const color = {
name: 'color',
Button: () => (
<LeafButton format="color">
<input onChange={onChange} type="color"></input>
</LeafButton>
),
Leaf: Color,
};
like that
ohhhhh
(its early)
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!
@jacobsfletch I saw you did a markdown plugin recently, maybe you can help identify why the span doesn't receive any style?
@jmikrut 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
Is anyone available to help out with this one?
Still stuck on adding the leaf
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!
TY @Jakey
I'll also try to learn some React so I can be more self suficient
import React from 'react'
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..
hmmmm
Interesting because I don't see the color change
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 @Jakey
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 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]) {
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
@Jakey 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
i thought "attributes" was something to store data
but yeah, no leads
:*(
HELLO
i am alive
trying to clarify a few things quick
1. 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
2.
attributes
in 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:
https://github.com/payloadcms/payload/tree/master/test/admin/components/richText/leaves/PurpleBackground
there are only a few small things that need to be modified from this example:
1. 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
2. Instead of just storing a boolean
true
/
false
on the leaf itself, you would store the hex value of the color that was selected by the user
then boom done
Thanks so much for the info @jmikrut
Ill try to build this out now!
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
@jmikrut 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 leafas 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?
1. 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
that is an
elementbutton
you are working with a
leafDang im striking out today
Ok ok, ill find that Leaf Button
Woot!
{ "text": "we are available", "color_picker": "#c02a2a" },
beautiful!!!!!!
that is
exactlywhat you need
Yall are the best
Cleaning it up and posting example
@thisisnotchris 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
https://gist.github.com/notchris/1d5f2db9a20d84cff61a8c54e05f2e65
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
@Jakey 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 @Alessio π£ 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?
@edsonv were you following the code in my gist?
Hi @thisisnotchris, 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
https://gist.github.com/notchris/7ff01a0584b5a20a4a8f45c665ad1dff
thats my current
@thisisnotchris 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 @thisisnotchris and get Error: Uncaught Error: The
useSlate
hook must be used inside the <Slate> component's context.
what am I doing wrong?
@thisisnotchris could you show your package.json file your Payload CMS project ?
Sure
@savelii 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",
oh weird
I didn't expect that, does it work now?
yeah exactly
@thisisnotchris 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 ..
@savelii it should be saving the color to the lead in the generated json
Star
Discord
online
Get help straight from the Payload team with an Enterprise License.