feat(richtext-lexical): allow replacing entire blocks with custom components (#9234)
With this PR, you can now customize the way that `blocks` and `inlineBlocks` are rendered within Lexical's `BlocksFeature` by passing your own React components. This is super helpful when you need to create "previews" or more accurate UI for your Lexical blocks. For example, let's say you have a `gallery` block where your admins select a bunch of images. By default, Lexical would just render a collapsible with your block's fields in it. But now you can customize the `admin.components.Block` property on your `block` config by passing it a custom React component for us to render instead. So using that, with this `gallery` example, you could make a dynamic gallery React component that shows the images to your editors - and then render our built-in `BlockEditButton` to allow your editors to manage your gallery in a drawer. Here is an example where the BlockEditButton is added to the default Block Collapsible/Header:  --------- Co-authored-by: James <james@trbl.design>
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
import {
|
||||
BlockCollapsible,
|
||||
BlockEditButton,
|
||||
BlockRemoveButton,
|
||||
} from '@payloadcms/richtext-lexical/client'
|
||||
import { useFormFields } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
export const BlockComponent: React.FC = () => {
|
||||
const key = useFormFields(([fields]) => fields.key)
|
||||
|
||||
return (
|
||||
<BlockCollapsible>
|
||||
MY BLOCK COMPONENT. Value: {(key?.value as string) ?? '<no value>'}
|
||||
Edit: <BlockEditButton />
|
||||
<BlockRemoveButton />
|
||||
</BlockCollapsible>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useFormFields } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
export const LabelComponent: React.FC = () => {
|
||||
const key = useFormFields(([fields]) => fields.key)
|
||||
|
||||
return <div>{(key?.value as string) ?? '<no value>'}yaya</div>
|
||||
}
|
||||
@@ -82,13 +82,137 @@ const editorConfig: ServerEditorConfig = {
|
||||
ConditionalLayoutBlock,
|
||||
TabBlock,
|
||||
CodeBlock,
|
||||
{
|
||||
slug: 'myBlock',
|
||||
admin: {
|
||||
components: {},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'myBlockWithLabel',
|
||||
admin: {
|
||||
components: {
|
||||
Label: '/collections/Lexical/blockComponents/LabelComponent.js#LabelComponent',
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'myBlockWithBlock',
|
||||
admin: {
|
||||
components: {
|
||||
Block: '/collections/Lexical/blockComponents/BlockComponent.js#BlockComponent',
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'myBlockWithBlockAndLabel',
|
||||
admin: {
|
||||
components: {
|
||||
Block: '/collections/Lexical/blockComponents/BlockComponent.js#BlockComponent',
|
||||
Label: '/collections/Lexical/blockComponents/LabelComponent.js#LabelComponent',
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
inlineBlocks: [
|
||||
{
|
||||
slug: 'myInlineBlock',
|
||||
admin: {
|
||||
components: {},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'myInlineBlockWithLabel',
|
||||
admin: {
|
||||
components: {
|
||||
Label: '/collections/Lexical/LabelComponent.js#LabelComponent',
|
||||
Label: '/collections/Lexical/inlineBlockComponents/LabelComponent.js#LabelComponent',
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'myInlineBlockWithBlock',
|
||||
admin: {
|
||||
components: {
|
||||
Block: '/collections/Lexical/inlineBlockComponents/BlockComponent.js#BlockComponent',
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: 'key',
|
||||
label: () => {
|
||||
return 'Key'
|
||||
},
|
||||
type: 'select',
|
||||
options: ['value1', 'value2', 'value3'],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'myInlineBlockWithBlockAndLabel',
|
||||
admin: {
|
||||
components: {
|
||||
Block: '/collections/Lexical/inlineBlockComponents/BlockComponent.js#BlockComponent',
|
||||
Label: '/collections/Lexical/inlineBlockComponents/LabelComponent.js#LabelComponent',
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import {
|
||||
InlineBlockContainer,
|
||||
InlineBlockEditButton,
|
||||
InlineBlockLabel,
|
||||
InlineBlockRemoveButton,
|
||||
} from '@payloadcms/richtext-lexical/client'
|
||||
import React from 'react'
|
||||
|
||||
export const BlockComponent: React.FC = () => {
|
||||
return (
|
||||
<InlineBlockContainer>
|
||||
<p>Test</p>
|
||||
<InlineBlockEditButton />
|
||||
<InlineBlockLabel />
|
||||
<InlineBlockRemoveButton />
|
||||
</InlineBlockContainer>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useFormFields } from '@payloadcms/ui'
|
||||
import React from 'react'
|
||||
|
||||
export const LabelComponent: React.FC = () => {
|
||||
const key = useFormFields(([fields]) => fields.key)
|
||||
|
||||
return <div>{(key?.value as string) ?? '<no value>'}yaya</div>
|
||||
}
|
||||
Reference in New Issue
Block a user