vault backup: 2026-05-15 16:37:52
This commit is contained in:
parent
d18653ff63
commit
f5b2c403c0
5 changed files with 335 additions and 1 deletions
|
|
@ -35,7 +35,7 @@ This 3-hop pattern works for hundreds of articles without vector search.
|
|||
| [[wiki/reports/_index\|reports/]] | Weekly and monthly summaries — generate: `uv run python scripts/report-generator.py --weekly` | 1 |
|
||||
| [[wiki/infrastructure/_index\|infrastructure/]] | Server inventory: all 10 SSH hosts — optical, optical-dev, optical-prod, baic, librechat, modocmms, box-cli, aimpress, pve | 12 |
|
||||
| [[wiki/testing/_index\|testing/]] | Web app testing: functional, performance, security, UI types; TDD/BDD/Agile methodologies; Selenium/Cypress/Playwright/JMeter/OWASP ZAP tools | 1 |
|
||||
| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization, hierarchy | 130 |
|
||||
| [[wiki/payloadcms/_index\|payloadcms/]] | Full Payload CMS reference — getting started, config, database (Postgres/MongoDB/SQLite), all 22 field types, access control, hooks, authentication (cookies, JWT, API keys, custom strategies, token data), admin UI, custom components, Lexical rich text, live preview, versions/drafts, Local/REST/GraphQL APIs, queries, plugins, jobs queue, upload, ecommerce, production deploy, TypeScript, migration guides, i18n, localization, hierarchy | 131 |
|
||||
|
||||
| [[wiki/shared-patterns/_index\|shared-patterns/]] | Oliver Agency standard library patterns: httpx, structlog, pydantic-settings, alembic — reuse before writing from scratch | 4 |
|
||||
| [[wiki/mistakes/_index\|mistakes/]] | Anti-patterns extracted from sessions — per-stack running lists (fastapi, react, docker, postgres, general) — injected at session start | 5 |
|
||||
|
|
|
|||
|
|
@ -131,3 +131,4 @@
|
|||
| [[wiki/payloadcms/queries-select\|Queries — Select & Populate]] | Include/exclude field selection at DB level, entity-level `select` function for hooks/access, `defaultPopulate` for relationship optimization, `populate` override | raw/queries__select.md | 2026-05-15 |
|
||||
| [[wiki/payloadcms/queries-sort\|Queries — Sort]] | Sort by any stored top-level field: `-field` descending, array (Local API) or comma-separated string (REST/GraphQL), index tip | raw/queries__sort.md | 2026-05-15 |
|
||||
| [[wiki/payloadcms/query-presets\|Query Presets]] | Save & share List View filters/columns/sort as DB records; collection-level + document-level access control; custom RBAC constraints; filterConstraints | raw/query-presets__overview.md | 2026-05-15 |
|
||||
| [[wiki/payloadcms/rich-text-blocks\|Rich Text — Blocks (Lexical BlocksFeature)]] | Embed Payload Blocks in Lexical rich text: block vs inline blocks, data structure, custom admin components (BlockCollapsible/BlockEditButton), pre-built CodeBlock with TS IntelliSense, frontend rendering | raw/rich-text__blocks.md | 2026-05-15 |
|
||||
|
|
|
|||
213
wiki/payloadcms/rich-text-blocks.md
Normal file
213
wiki/payloadcms/rich-text-blocks.md
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
---
|
||||
title: "Rich Text — Blocks (Lexical BlocksFeature)"
|
||||
aliases: [lexical-blocks, rich-text-blocks, blocksfeature]
|
||||
tags: [payloadcms, rich-text, lexical, blocks, inline-blocks]
|
||||
sources: [raw/rich-text__blocks.md]
|
||||
created: 2026-05-15
|
||||
updated: 2026-05-15
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
`BlocksFeature` embeds Payload's [[wiki/payloadcms/fields-blocks|Blocks Field]] directly inside the Lexical rich text editor. Blocks share the same config schema as standard Payload blocks — all field types, hooks, validation, access control, and conditional logic are supported. The only difference is that data is stored inside the rich text JSON, not as separate DB fields.
|
||||
|
||||
## Basic Setup
|
||||
|
||||
```ts
|
||||
import { lexicalEditor, BlocksFeature } from '@payloadcms/richtext-lexical'
|
||||
|
||||
{
|
||||
name: 'content',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
BlocksFeature({
|
||||
blocks: [
|
||||
{
|
||||
slug: 'banner',
|
||||
fields: [
|
||||
{ name: 'style', type: 'select', options: ['info', 'warning', 'error', 'success'], defaultValue: 'info' },
|
||||
{ name: 'content', type: 'textarea', required: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
}
|
||||
```
|
||||
|
||||
## Blocks vs Inline Blocks
|
||||
|
||||
| | **Blocks** | **Inline Blocks** |
|
||||
|--|--|--|
|
||||
| Position | Block-level — occupy a full line | Flow within text (same paragraph) |
|
||||
| Use cases | CTAs, galleries, code snippets, embeds | Mentions, badges, icons, placeholders, footnotes |
|
||||
| Config key | `blocks: []` | `inlineBlocks: []` |
|
||||
|
||||
```ts
|
||||
BlocksFeature({
|
||||
blocks: [
|
||||
{ slug: 'callout', fields: [{ name: 'content', type: 'textarea' }] },
|
||||
],
|
||||
inlineBlocks: [
|
||||
{ slug: 'mention', fields: [{ name: 'user', type: 'relationship', relationTo: 'users', required: true }] },
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
## Data Structure
|
||||
|
||||
Block nodes in the Lexical JSON contain a `fields` object. Three fields are auto-included:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "block",
|
||||
"version": 2,
|
||||
"fields": {
|
||||
"id": "65298b13db4ef8c744a7faaa",
|
||||
"blockType": "banner",
|
||||
"blockName": "Important Notice",
|
||||
"style": "warning",
|
||||
"content": "This is the block content..."
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Inline blocks use `"type": "inlineBlock"` but follow the same shape.
|
||||
|
||||
## Custom Block Components
|
||||
|
||||
Override how a block renders in the admin editor via `admin.components.Block`.
|
||||
|
||||
### Block Component (client)
|
||||
|
||||
```ts
|
||||
{
|
||||
slug: 'myCustomBlock',
|
||||
admin: {
|
||||
components: {
|
||||
Block: '/path/to/MyBlockComponent#MyBlockComponent',
|
||||
},
|
||||
},
|
||||
fields: [{ name: 'style', type: 'select', options: ['primary', 'secondary'] }],
|
||||
}
|
||||
```
|
||||
|
||||
Composable primitives from `@payloadcms/richtext-lexical/client`:
|
||||
|
||||
| Primitive | Purpose |
|
||||
|-----------|---------|
|
||||
| `BlockCollapsible` | Collapsible wrapper with built-in edit drawer trigger |
|
||||
| `BlockEditButton` | Opens field edit drawer |
|
||||
| `BlockRemoveButton` | Removes the block from the editor |
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
import type { LexicalBlockClientProps } from '@payloadcms/richtext-lexical'
|
||||
import { BlockCollapsible, BlockEditButton, BlockRemoveButton } from '@payloadcms/richtext-lexical/client'
|
||||
import { useFormFields } from '@payloadcms/ui'
|
||||
|
||||
export const MyBlockComponent: React.FC<LexicalBlockClientProps> = () => {
|
||||
const style = useFormFields(([fields]) => fields.style)
|
||||
return (
|
||||
<BlockCollapsible removeButton={false}>
|
||||
<div>Style: {(style?.value as string) ?? 'none'}</div>
|
||||
<div style={{ display: 'flex' }}>
|
||||
<BlockEditButton />
|
||||
<BlockRemoveButton />
|
||||
</div>
|
||||
</BlockCollapsible>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Inline Block Component (client)
|
||||
|
||||
Primitives: `InlineBlockContainer`, `InlineBlockEditButton`, `InlineBlockLabel`, `InlineBlockRemoveButton` — all from `@payloadcms/richtext-lexical/client`.
|
||||
|
||||
### Label Components
|
||||
|
||||
Customize the block header label via `admin.components.Label`. Use `useFormFields` to render dynamic content from block field values.
|
||||
|
||||
## TypeScript Types
|
||||
|
||||
```ts
|
||||
import type {
|
||||
LexicalBlockClientProps,
|
||||
LexicalBlockServerProps,
|
||||
LexicalBlockLabelClientProps,
|
||||
LexicalBlockLabelServerProps,
|
||||
LexicalInlineBlockClientProps,
|
||||
LexicalInlineBlockServerProps,
|
||||
LexicalInlineBlockLabelClientProps,
|
||||
LexicalInlineBlockLabelServerProps,
|
||||
} from '@payloadcms/richtext-lexical'
|
||||
```
|
||||
|
||||
## Pre-built CodeBlock
|
||||
|
||||
Payload ships a ready-made `CodeBlock` with syntax highlighting, language selection, and optional TypeScript IntelliSense:
|
||||
|
||||
```ts
|
||||
import { BlocksFeature, CodeBlock } from '@payloadcms/richtext-lexical'
|
||||
|
||||
BlocksFeature({
|
||||
blocks: [
|
||||
CodeBlock({
|
||||
defaultLanguage: 'ts',
|
||||
languages: { plaintext: 'Plain Text', js: 'JavaScript', ts: 'TypeScript', tsx: 'TSX', jsx: 'JSX' },
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
### CodeBlock Options
|
||||
|
||||
| Option | Description |
|
||||
|--------|-------------|
|
||||
| `slug` | Override slug (default: `'Code'`) |
|
||||
| `defaultLanguage` | Default language (default: first key) |
|
||||
| `languages` | `{ key: label }` map |
|
||||
| `typescript` | TypeScript IntelliSense config (see below) |
|
||||
| `fieldOverrides` | Partial block config to extend defaults |
|
||||
|
||||
### TypeScript IntelliSense
|
||||
|
||||
```ts
|
||||
CodeBlock({
|
||||
typescript: {
|
||||
fetchTypes: [
|
||||
{ url: 'https://unpkg.com/payload@latest/dist/index.bundled.d.ts', filePath: 'file:///node_modules/payload/index.d.ts' },
|
||||
],
|
||||
paths: { payload: ['file:///node_modules/payload/index.d.ts'] },
|
||||
typeRoots: ['node_modules/@types', 'node_modules/payload'],
|
||||
enableSemanticValidation: true,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
## Rendering Blocks on the Frontend
|
||||
|
||||
Blocks must be handled in your converter config:
|
||||
|
||||
- **JSX Converters** — React/Next.js: [[wiki/payloadcms/rich-text|Rich Text (Lexical)]] converting-jsx guide
|
||||
- **HTML Converters** — static HTML output
|
||||
- **Markdown Converters** — define custom block renderers
|
||||
|
||||
Each converter lets you define per-block-type custom renderers.
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- `BlocksFeature` accepts `blocks` (block-level) and `inlineBlocks` (inline) arrays — same config schema as the standard [[wiki/payloadcms/fields-blocks|Blocks Field]]
|
||||
- Block data lives inside Lexical JSON; three auto-fields are always present: `id`, `blockType`, `blockName`
|
||||
- Custom admin components use composable primitives (`BlockCollapsible`, `BlockEditButton`, etc.) from `@payloadcms/richtext-lexical/client`
|
||||
- Use `useFormFields` (from `@payloadcms/ui`) to read live block field values inside custom components
|
||||
- Pre-built `CodeBlock` covers most code-display needs, with optional TS IntelliSense via `fetchTypes`
|
||||
- Frontend rendering requires explicit block handlers in your JSX/HTML/Markdown converter config
|
||||
- See [Payload's CodeBlock source](https://github.com/payloadcms/payload/blob/main/packages/richtext-lexical/src/features/blocks/premade/CodeBlock/index.ts) for a real-world custom block example
|
||||
|
||||
## Sources
|
||||
|
||||
- `raw/rich-text__blocks.md` (https://payloadcms.com/docs/rich-text/blocks)
|
||||
120
wiki/payloadcms/rich-text-converters.md
Normal file
120
wiki/payloadcms/rich-text-converters.md
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
title: "Rich Text — Converters (Lexical)"
|
||||
aliases: [lexical-converters, richtext-converters, lexical-export]
|
||||
tags: [payloadcms, lexical, rich-text, converters, html, markdown, jsx, plaintext]
|
||||
sources: [raw/rich-text__converters.md]
|
||||
created: 2026-05-15
|
||||
updated: 2026-05-15
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Lexical rich text fields store data as **JSON**. Payload provides converters to transform that JSON into other formats for rendering or export.
|
||||
|
||||
## Supported Output Formats
|
||||
|
||||
| Format | Reference |
|
||||
|--------|-----------|
|
||||
| JSX | `/docs/rich-text/converting-jsx` |
|
||||
| HTML | `/docs/rich-text/converting-html` |
|
||||
| Plaintext | `/docs/rich-text/converting-plaintext` |
|
||||
| Markdown / MDX | `/docs/rich-text/converting-markdown` |
|
||||
|
||||
## Retrieving the Editor Config
|
||||
|
||||
Some converters need access to the **Lexical editor config** (defines available features/nodes). Use `editorConfigFactory` from `@payloadcms/richtext-lexical`.
|
||||
|
||||
```ts
|
||||
import { editorConfigFactory } from '@payloadcms/richtext-lexical'
|
||||
```
|
||||
|
||||
### Option 1 — Default config
|
||||
|
||||
```ts
|
||||
const editorConfig = await editorConfigFactory.default({ config })
|
||||
```
|
||||
|
||||
### Option 2 — Extract from a field
|
||||
|
||||
When you have a sanitized field reference (e.g. inside a hook on a sibling field):
|
||||
|
||||
```ts
|
||||
const editorConfig = editorConfigFactory.fromField({ field })
|
||||
```
|
||||
|
||||
### Option 3 — Custom features config
|
||||
|
||||
```ts
|
||||
import { FixedToolbarFeature } from '@payloadcms/richtext-lexical'
|
||||
|
||||
const editorConfig = await editorConfigFactory.fromFeatures({
|
||||
config,
|
||||
features: ({ defaultFeatures }) => [...defaultFeatures, FixedToolbarFeature()],
|
||||
})
|
||||
```
|
||||
|
||||
### Option 4 — From an instantiated editor (not recommended)
|
||||
|
||||
```ts
|
||||
const editorConfig = await editorConfigFactory.fromEditor({ config, editor })
|
||||
```
|
||||
|
||||
> Prefer `fromFeatures` — extract features into a variable and reuse.
|
||||
|
||||
## Practical Pattern — Get Config in a Hook
|
||||
|
||||
Access the lexical editor config of a sibling `richText` field from an `afterRead` hook on another field:
|
||||
|
||||
```ts
|
||||
import type { CollectionConfig, RichTextField } from 'payload'
|
||||
import { editorConfigFactory, lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
export const MyCollection: CollectionConfig = {
|
||||
slug: 'slug',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
hooks: {
|
||||
afterRead: [
|
||||
({ siblingFields, value }) => {
|
||||
const richTextField = siblingFields.find(
|
||||
(f) => 'name' in f && f.name === 'richText',
|
||||
) as RichTextField
|
||||
|
||||
const editorConfig = editorConfigFactory.fromField({ field: richTextField })
|
||||
// use editorConfig to run a converter
|
||||
return value
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'richText',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor(),
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## Key Takeaways
|
||||
|
||||
- Lexical stores rich text as **JSON** — converters transform it to HTML/JSX/Markdown/Plaintext.
|
||||
- Always import `editorConfigFactory` from `@payloadcms/richtext-lexical`.
|
||||
- Four factory methods: `default`, `fromField`, `fromFeatures`, `fromEditor` — prefer `fromFeatures` for custom setups.
|
||||
- Access a sibling field's editor config via `editorConfigFactory.fromField({ field })` inside hooks.
|
||||
- `fromEditor` is least efficient — avoid unless you already have an instantiated editor.
|
||||
|
||||
## Related
|
||||
|
||||
- [[wiki/payloadcms/rich-text|Rich Text (Lexical) — Overview]]
|
||||
- [[wiki/payloadcms/rich-text-blocks|Rich Text — Blocks (BlocksFeature)]]
|
||||
- [[wiki/payloadcms/fields-rich-text|Rich Text Field Config]]
|
||||
- [[wiki/payloadcms/hooks-fields|Field Hooks]]
|
||||
- [[wiki/payloadcms/hooks-collections|Collection Hooks]]
|
||||
|
||||
## Sources
|
||||
|
||||
- `raw/rich-text__converters.md`
|
||||
- https://payloadcms.com/docs/rich-text/converters
|
||||
Loading…
Add table
Reference in a new issue