fix(richtext-lexical)!: fix output of internal list HTML converter (#5827)

BREAKING: Changes the classnames of the converted HTML
This commit is contained in:
Alessio Gravili
2024-04-12 12:10:44 -04:00
committed by GitHub
4 changed files with 51 additions and 11 deletions

View File

@@ -235,6 +235,19 @@ This method employs `convertLexicalToHTML` from `@payloadcms/richtext-lexical`,
Because every `Feature` is able to provide html converters, and because the `htmlFeature` can modify those or provide their own, we need to consolidate them with the default html Converters using the `consolidateHTMLConverters` function.
#### CSS
Payload's lexical HTML converter does not generate CSS for you, but it does add classes to the generated HTML. You can use these classes to style the HTML in your frontend.
Here is some "base" CSS you can use to ensure that nested lists render correctly:
```css
/* Base CSS for Lexical HTML */
.nestedListItem, .list-check {
list-style-type: none;
}
```
#### Creating your own HTML Converter
HTML Converters are typed as `HTMLConverter`, which contains the node type it should handle, and a function that accepts the serialized node from the lexical editor, and outputs the HTML string. Here's the HTML Converter of the Upload node as an example:

View File

@@ -1,5 +1,5 @@
import type { SerializedEditorState } from 'lexical'
import type { Field, RichTextField, TextField } from 'payload/types'
import type { Field, RichTextField } from 'payload/types'
import type { AdapterProps, LexicalRichTextAdapter } from '../../../../../types.js'
import type { SanitizedServerEditorConfig } from '../../../../lexical/config/types.js'
@@ -10,6 +10,12 @@ import { defaultHTMLConverters } from '../converter/defaultConverters.js'
import { convertLexicalToHTML } from '../converter/index.js'
type Props = {
/**
* Whether the lexicalHTML field should be hidden in the admin panel
*
* @default true
*/
hidden?: boolean
name: string
}
@@ -53,13 +59,16 @@ export const lexicalHTML: (
**/
lexicalFieldName: string,
props: Props,
) => TextField = (lexicalFieldName, props) => {
const { name = 'lexicalHTML' } = props
) => Field = (lexicalFieldName, props) => {
const { name = 'lexicalHTML', hidden = true } = props
return {
name,
type: 'text',
type: 'code',
admin: {
hidden: true,
editorOptions: {
language: 'html',
},
hidden,
},
hooks: {
afterRead: [

View File

@@ -1,6 +1,7 @@
import type { SerializedListItemNode, SerializedListNode } from '@lexical/list'
import lexicalListImport from '@lexical/list'
import { v4 as uuidv4 } from 'uuid'
const { ListItemNode, ListNode } = lexicalListImport
import type { HTMLConverter } from '../converters/html/converter/types.js'
@@ -19,13 +20,15 @@ export const ListHTMLConverter: HTMLConverter<SerializedListNode> = {
payload,
})
return `<${node?.tag} class="${node?.listType}">${childrenText}</${node?.tag}>`
return `<${node?.tag} class="list-${node?.listType}">${childrenText}</${node?.tag}>`
},
nodeTypes: [ListNode.getType()],
}
export const ListItemHTMLConverter: HTMLConverter<SerializedListItemNode> = {
converter: async ({ converters, node, parent, payload }) => {
const hasSubLists = node.children.some((child) => child.type === 'list')
const childrenText = await convertLexicalNodesToHTML({
converters,
lexicalNodes: node.children,
@@ -37,19 +40,30 @@ export const ListItemHTMLConverter: HTMLConverter<SerializedListItemNode> = {
})
if ('listType' in parent && parent?.listType === 'check') {
const uuid = uuidv4()
return `<li aria-checked=${node.checked ? 'true' : 'false'} class="${
'list-item-checkbox' + node.checked
? 'list-item-checkbox-checked'
: 'list-item-checkbox-unchecked'
'list-item-checkbox' +
(node.checked ? ' list-item-checkbox-checked' : ' list-item-checkbox-unchecked') +
(hasSubLists ? ' nestedListItem' : '')
}"
role="checkbox"
tabIndex=${-1}
value=${node?.value}
>
${childrenText}
${
hasSubLists
? childrenText
: `
<input type="checkbox" id="${uuid}"${node.checked ? ' checked' : ''}>
<label for="${uuid}">${childrenText}</label><br>
`
}
</li>`
} else {
return `<li value=${node?.value}>${childrenText}</li>`
return `<li ${hasSubLists ? `class="nestedListItem" ` : ''}value=${node?.value}>${childrenText}</li>`
}
},
nodeTypes: [ListItemNode.getType()],

View File

@@ -331,6 +331,10 @@
list-style-position: inside;
}
&__ul ul {
margin: 0;
}
&__listItem {
margin: 0 0px 0.4em 16px;
}