eddev 2.3.12 → 2.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/entry/MetaTags.d.ts +1 -1
- package/dist/app/entry/MetaTags.d.ts.map +1 -1
- package/dist/app/entry/MetaTags.js +2 -0
- package/dist/app/entry/spa-root.d.ts +1 -1
- package/dist/app/entry/spa-root.d.ts.map +1 -1
- package/dist/app/entry/ssr-root-client.d.ts +1 -1
- package/dist/app/entry/ssr-root-client.d.ts.map +1 -1
- package/dist/app/entry/ssr-root.d.ts +1 -1
- package/dist/app/entry/ssr-root.d.ts.map +1 -1
- package/dist/app/lib/blocks/BlockPropMutator.d.ts +1 -1
- package/dist/app/lib/blocks/BlockPropMutator.d.ts.map +1 -1
- package/dist/app/lib/blocks/ContentBlocks.d.ts +2 -2
- package/dist/app/lib/blocks/ContentBlocks.d.ts.map +1 -1
- package/dist/app/lib/blocks/EditableText.d.ts +1 -1
- package/dist/app/lib/blocks/EditableText.d.ts.map +1 -1
- package/dist/app/lib/blocks/InnerBlocks.d.ts +1 -1
- package/dist/app/lib/blocks/InnerBlocks.d.ts.map +1 -1
- package/dist/app/lib/blocks/SlotBlocks.d.ts +1 -1
- package/dist/app/lib/blocks/SlotBlocks.d.ts.map +1 -1
- package/dist/app/lib/blocks/block-debugger.d.ts +3 -3
- package/dist/app/lib/blocks/block-debugger.d.ts.map +1 -1
- package/dist/app/lib/blocks/block-utils.d.ts +2 -2
- package/dist/app/lib/blocks/block-utils.d.ts.map +1 -1
- package/dist/app/lib/blocks/block-utils.js +10 -3
- package/dist/app/lib/blocks/editor/EditableBlock.d.ts +1 -1
- package/dist/app/lib/blocks/editor/EditableBlock.d.ts.map +1 -1
- package/dist/app/lib/blocks/editor/EditorHighlights.d.ts +1 -1
- package/dist/app/lib/blocks/editor/EditorHighlights.d.ts.map +1 -1
- package/dist/app/lib/blocks/editor/ErrorBoundaryEditor.d.ts +1 -1
- package/dist/app/lib/blocks/editor/controls.d.ts +2 -2
- package/dist/app/lib/blocks/editor/controls.d.ts.map +1 -1
- package/dist/app/lib/blocks/editor/installGutenbergHooks.d.ts.map +1 -1
- package/dist/app/lib/blocks/editor/installGutenbergHooks.js +8 -4
- package/dist/app/lib/blocks/editor/root-blocks.d.ts.map +1 -1
- package/dist/app/lib/blocks/editor/root-blocks.js +2 -2
- package/dist/app/lib/blocks/inline-editing.d.ts +2 -2
- package/dist/app/lib/blocks/inline-editing.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/BreakpointIndicator.d.ts +1 -1
- package/dist/app/lib/devtools/components/BreakpointIndicator.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/DevMenu.d.ts +1 -1
- package/dist/app/lib/devtools/components/DevMenu.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/DevUI.d.ts +2 -2
- package/dist/app/lib/devtools/components/DevUI.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/panels/AppDataPanel.d.ts +2 -2
- package/dist/app/lib/devtools/components/panels/AppDataPanel.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/panels/QueriesPanel.d.ts +1 -1
- package/dist/app/lib/devtools/components/panels/QueriesPanel.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/panels/RoutePanel.d.ts +2 -2
- package/dist/app/lib/devtools/components/panels/RoutePanel.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/panels/StructurePanel.d.ts +1 -1
- package/dist/app/lib/devtools/components/panels/StructurePanel.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/Button.d.ts +1 -1
- package/dist/app/lib/devtools/components/ui/Button.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/Chip.d.ts +2 -2
- package/dist/app/lib/devtools/components/ui/Chip.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/Expander.d.ts +1 -1
- package/dist/app/lib/devtools/components/ui/Expander.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/JSONInspector.d.ts +1 -1
- package/dist/app/lib/devtools/components/ui/JSONInspector.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/Panel.d.ts +1 -1
- package/dist/app/lib/devtools/components/ui/Panel.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/QueryMonitorDisplay.d.ts +1 -1
- package/dist/app/lib/devtools/components/ui/QueryMonitorDisplay.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/TabBar.d.ts +2 -2
- package/dist/app/lib/devtools/components/ui/TabBar.d.ts.map +1 -1
- package/dist/app/lib/devtools/components/ui/Tabs.d.ts +1 -1
- package/dist/app/lib/devtools/components/ui/Tabs.d.ts.map +1 -1
- package/dist/app/lib/devtools/hooks/useTailwind.d.ts +45 -45
- package/dist/app/lib/devtools/icons.d.ts +24 -24
- package/dist/app/lib/devtools/icons.d.ts.map +1 -1
- package/dist/app/lib/hooks/query-hooks.d.ts +8 -8
- package/dist/app/lib/hooks/query-hooks.d.ts.map +1 -1
- package/dist/app/lib/hooks/query-hooks.js +2 -1
- package/dist/app/lib/integrations/gravityforms/createGravityFormComponent.d.ts +15 -15
- package/dist/app/lib/routing/components/BrowserRouter.d.ts +1 -1
- package/dist/app/lib/routing/components/BrowserRouter.d.ts.map +1 -1
- package/dist/app/lib/routing/components/ClientOnly.d.ts +1 -1
- package/dist/app/lib/routing/components/ClientOnly.d.ts.map +1 -1
- package/dist/app/lib/routing/components/NativeLinkHandler.d.ts +1 -1
- package/dist/app/lib/routing/components/NativeLinkHandler.d.ts.map +1 -1
- package/dist/app/lib/routing/components/RouteRenderer.d.ts +3 -3
- package/dist/app/lib/routing/components/RouteRenderer.d.ts.map +1 -1
- package/dist/app/lib/routing/components/SSRRouter.d.ts +1 -1
- package/dist/app/lib/routing/components/SSRRouter.d.ts.map +1 -1
- package/dist/app/lib/runtime/apiConfig.d.ts +15 -0
- package/dist/app/lib/runtime/apiConfig.d.ts.map +1 -1
- package/dist/app/server/proxy-wp-admin.d.ts.map +1 -1
- package/dist/app/server/proxy-wp-admin.js +1 -0
- package/dist/app/server/server-context.d.ts +2 -2
- package/dist/app/server/server-context.d.ts.map +1 -1
- package/dist/app/utils/APIProvider.d.ts +1 -1
- package/dist/app/utils/APIProvider.d.ts.map +1 -1
- package/dist/app/utils/ErrorMessage.d.ts +1 -1
- package/dist/app/utils/ErrorMessage.d.ts.map +1 -1
- package/dist/app/utils/query-client.d.ts +1 -1
- package/dist/app/utils/query-client.d.ts.map +1 -1
- package/dist/app/utils/trpc-client.d.ts.map +1 -1
- package/dist/app/utils/trpc-client.js +19 -3
- package/dist/node/cli/display/CLIApp.d.ts +1 -1
- package/dist/node/cli/display/CLIApp.d.ts.map +1 -1
- package/dist/node/cli/display/components/Fullscreen.d.ts +1 -1
- package/dist/node/cli/display/components/Fullscreen.d.ts.map +1 -1
- package/dist/node/cli/display/components/LogEntries.d.ts +4 -4
- package/dist/node/cli/display/components/LogEntries.d.ts.map +1 -1
- package/dist/node/cli/display/components/MenuItem.d.ts +1 -1
- package/dist/node/cli/display/components/MenuItem.d.ts.map +1 -1
- package/dist/node/cli/display/components/TextInput.d.ts +3 -2
- package/dist/node/cli/display/components/TextInput.d.ts.map +1 -1
- package/dist/node/cli/display/tools/BlockList.d.ts +1 -1
- package/dist/node/cli/display/tools/BlockList.d.ts.map +1 -1
- package/dist/node/cli/display/tools/CreateBlock.d.ts +1 -1
- package/dist/node/cli/display/tools/CreateBlock.d.ts.map +1 -1
- package/dist/node/cli/version.d.ts +1 -1
- package/dist/node/cli/version.js +1 -1
- package/dist/node/compiler/dev-server.js +1 -1
- package/dist/node/compiler/get-vite-config.d.ts.map +1 -1
- package/dist/node/compiler/get-vite-config.js +1 -0
- package/dist/node/compiler/vinxi-app.d.ts +1 -1
- package/dist/node/compiler/vinxi-app.d.ts.map +1 -1
- package/dist/node/utils/fetch-wp.d.ts.map +1 -1
- package/dist/node/utils/fetch-wp.js +2 -1
- package/dist/node/utils/fs.d.ts +22 -19
- package/dist/node/utils/fs.d.ts.map +1 -1
- package/package.json +2 -2
- package/skills/eddev/SKILL.md +156 -0
- package/skills/eddev/docs/acf/admin-panel-widgets.mdx +99 -0
- package/skills/eddev/docs/acf/custom-enums.mdx +75 -0
- package/skills/eddev/docs/acf/custom-fields.mdx +131 -0
- package/skills/eddev/docs/acf.mdx +31 -0
- package/skills/eddev/docs/blocks/block-definition.mdx +189 -0
- package/skills/eddev/docs/blocks/core-blocks.mdx +86 -0
- package/skills/eddev/docs/blocks/data-and-editing.mdx +219 -0
- package/skills/eddev/docs/blocks/editor-config.mdx +157 -0
- package/skills/eddev/docs/blocks/nested-blocks.mdx +129 -0
- package/skills/eddev/docs/blocks/overview.mdx +58 -0
- package/skills/eddev/docs/blocks/template-parts.mdx +131 -0
- package/skills/eddev/docs/config.mdx +200 -0
- package/skills/eddev/docs/design/color.mdx +185 -0
- package/skills/eddev/docs/design/favicons.mdx +103 -0
- package/skills/eddev/docs/design/grid.mdx +120 -0
- package/skills/eddev/docs/design/icons.mdx +197 -0
- package/skills/eddev/docs/design/responsive-scaling.mdx +312 -0
- package/skills/eddev/docs/design/type.mdx +125 -0
- package/skills/eddev/docs/devtool/cli.mdx +201 -0
- package/skills/eddev/docs/devtool/overlay.mdx +5 -0
- package/skills/eddev/docs/getting-started.mdx +53 -0
- package/skills/eddev/docs/graphql/extending.mdx +186 -0
- package/skills/eddev/docs/graphql/fragments.mdx +107 -0
- package/skills/eddev/docs/graphql/global-data.mdx +47 -0
- package/skills/eddev/docs/graphql/infinite-queries.mdx +95 -0
- package/skills/eddev/docs/graphql/mutation-hooks.mdx +111 -0
- package/skills/eddev/docs/graphql/query-hooks.mdx +122 -0
- package/skills/eddev/docs/graphql/tooling.mdx +50 -0
- package/skills/eddev/docs/graphql.mdx +97 -0
- package/skills/eddev/docs/guides/color-schemes.mdx +204 -0
- package/skills/eddev/docs/guides/integrations.mdx +3 -0
- package/skills/eddev/docs/guides/page-transitions.mdx +5 -0
- package/skills/eddev/docs/guides/seo.mdx +5 -0
- package/skills/eddev/docs/guides/state-management.mdx +5 -0
- package/skills/eddev/docs/infra/caching.mdx +9 -0
- package/skills/eddev/docs/infra/deployment.mdx +13 -0
- package/skills/eddev/docs/infra/local.mdx +5 -0
- package/skills/eddev/docs/infra/security.mdx +11 -0
- package/skills/eddev/docs/routing/api.mdx +731 -0
- package/skills/eddev/docs/routing/custom.mdx +123 -0
- package/skills/eddev/docs/routing/full-details.mdx +37 -0
- package/skills/eddev/docs/routing/wordpress.mdx +70 -0
- package/skills/eddev/docs/routing.mdx +18 -0
- package/skills/eddev/docs/serverless/functions.mdx +436 -0
- package/skills/eddev/docs/serverless.mdx +202 -0
- package/skills/eddev/docs/snippets/automated-block-layouts.mdx +97 -0
- package/skills/eddev/docs/snippets/custom-routes-and-urls.mdx +91 -0
- package/skills/eddev/docs/snippets/multiple-editable-zones.mdx +87 -0
- package/skills/eddev/docs/snippets/querying-specific-blocks.mdx +164 -0
- package/skills/eddev/docs/snippets/submitting-forms-to-rpc.mdx +91 -0
- package/skills/eddev/docs/snippets/svgs.mdx +38 -0
- package/skills/eddev/docs/snippets/type-safe-acf-dropdowns.mdx +72 -0
- package/skills/eddev/docs/snippets.mdx +19 -0
- package/skills/eddev/docs/software.mdx +19 -0
- package/skills/eddev/docs/stack/how-it-works.mdx +50 -0
- package/skills/eddev/docs/stack/overview.mdx +56 -0
- package/skills/eddev/docs/stack/spa-vs-ssr.mdx +52 -0
- package/skills/eddev/docs/views/app-view.mdx +97 -0
- package/skills/eddev/docs/views/page-templates.mdx +82 -0
- package/skills/eddev/docs/views/queries.mdx +116 -0
- package/skills/eddev/docs/views.mdx +63 -0
- package/skills/eddev/index.mdx +79 -0
- package/tsconfig.app.json +2 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Defining a block (/docs/blocks/block-definition)
|
|
2
|
+
|
|
3
|
+
**How to create a new block type**
|
|
4
|
+
|
|
5
|
+
Block types are automatically registered when `.tsx` files are created in the `blocks` folder.
|
|
6
|
+
|
|
7
|
+
Most block files have two exports: a `meta` object for Gutenberg/editor metadata, and a default export created with `defineBlock`.
|
|
8
|
+
|
|
9
|
+
```tsx filename="blocks/content/hello-world.tsx"
|
|
10
|
+
import { defineBlock } from "eddev/blocks"
|
|
11
|
+
|
|
12
|
+
export const meta: BlockMeta = {
|
|
13
|
+
title: "Hello World",
|
|
14
|
+
icon: "welcome-write-blog",
|
|
15
|
+
tags: ["#root"],
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default defineBlock("content/hello-world", (props) => {
|
|
19
|
+
return <div>Hello!</div>
|
|
20
|
+
})
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## `defineBlock` [#defineblock]
|
|
24
|
+
|
|
25
|
+
`defineBlock` returns the component you pass in, but its first argument matters for TypeScript. The name should match the file path under `blocks/`, without the extension.
|
|
26
|
+
|
|
27
|
+
For `blocks/content/hello-world.tsx`, use:
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
defineBlock("content/hello-world", (props) => {
|
|
31
|
+
return null
|
|
32
|
+
})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
That name connects the component to generated `BlockProps`, which are built from the paired GraphQL query in `types.blocks.ts`.
|
|
36
|
+
|
|
37
|
+
## Block Meta [#block-meta]
|
|
38
|
+
|
|
39
|
+
Exporting `meta`, typed with the global `BlockMeta` type, gives eddev enough information to register the block with ACF/Gutenberg and to expose lightweight metadata at runtime.
|
|
40
|
+
|
|
41
|
+
### Everyday Fields [#everyday-fields]
|
|
42
|
+
|
|
43
|
+
* `title: string{:ts}` — The title of this block, shown in the editor.
|
|
44
|
+
* `description?: string{:ts}` — Short helper text shown in the editor.
|
|
45
|
+
* `category?: "text" | "media" | "design" | "widgets" | "theme" | "embed" | "layouts"{:ts}` — The inserter category.
|
|
46
|
+
* `icon?: BlockIcon{:ts}` — WordPress icon name for the inserter.
|
|
47
|
+
* `keywords?: string[] | string{:ts}` — Extra search terms for authors.
|
|
48
|
+
* `tags?: string[]{:ts}` — eddev placement tags such as `#root`, `#inline`, or a site-specific tag.
|
|
49
|
+
* `flags?: Record<string, any>{:ts}` — Small runtime hints used by wrappers, spacing, or editor styling.
|
|
50
|
+
* `conditions?: object{:ts}` — Limits availability by post type, template, parent, or ancestor.
|
|
51
|
+
* `editMode?: "preview" | "edit" | "both"{:ts}` — Controls whether ACF fields show as sidebar-only, edit-only, or toggleable.
|
|
52
|
+
* `blockStyles?: { name: string; label: string; isDefault?: boolean }[]{:ts}` — Gutenberg style variants for a block.
|
|
53
|
+
|
|
54
|
+
### Conditions [#conditions]
|
|
55
|
+
|
|
56
|
+
Use `conditions` when a block should only be available in specific places:
|
|
57
|
+
|
|
58
|
+
```tsx filename="blocks/content/card-grid.tsx"
|
|
59
|
+
export const meta: BlockMeta = {
|
|
60
|
+
title: "Card Grid",
|
|
61
|
+
icon: "grid-view",
|
|
62
|
+
tags: ["#subpage"],
|
|
63
|
+
conditions: {
|
|
64
|
+
postTypes: ["page"],
|
|
65
|
+
templates: ["default"],
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`postTypes` limits the block by WordPress post type. `templates` limits it by page template and implies the `page` post type. `parents` and `ancestors` are available for stricter nested-block constraints, but prefer `InnerBlocks allowedBlocks` for simple parent-child relationships.
|
|
71
|
+
|
|
72
|
+
## Block Tags [#block-tags]
|
|
73
|
+
|
|
74
|
+
Since different blocks have different contexts, and are sometimes specific to certain page templates, post types, or sections of a page — it can be helpful to give these contexts some kind of nickname. This is where tags come in. Note that this is not a WordPress thing, but rather something specific to the eddev stack.
|
|
75
|
+
|
|
76
|
+
The tagging system accepts any string, but our convention is to prefix authoring-context tags with `#`. You may still see older unprefixed tags in existing projects.
|
|
77
|
+
|
|
78
|
+
We typically use a couple of tags by default:
|
|
79
|
+
|
|
80
|
+
* `#inline` for basic WYSIWYG blocks, like core headings/paragraphs/lists
|
|
81
|
+
* `#root` for blocks that can be added at a top level for a page
|
|
82
|
+
|
|
83
|
+
For a hypothetical content-heavy site, with homepages, landing pages, subpages, and a blog, you might have a set of tags like:
|
|
84
|
+
|
|
85
|
+
* `#homepage-root` — blocks that can only be added to the homepage
|
|
86
|
+
* `#landing-root` — full-bleed blocks that can be added the root of a landing page
|
|
87
|
+
* `#subpage-root` — blocks that work within the subpage grid (eg, 9 cols on desktop, 12 cols on mobile)
|
|
88
|
+
* `#article-root` — blocks that can be used on an article page
|
|
89
|
+
|
|
90
|
+
We might also have additional tags for nested blocks, like a `#button` tag that can be attached to a 'Button Link' block, as well as an 'Add to Cart' button block.
|
|
91
|
+
|
|
92
|
+
These tags will be referenced:
|
|
93
|
+
|
|
94
|
+
* In the `allowedBlocks` prop of `InnerBlocks`
|
|
95
|
+
* In the `rootBlocks` parameter when [configuring the editor](./editor-config) for a template or post type
|
|
96
|
+
|
|
97
|
+
## Block Flags [#block-flags]
|
|
98
|
+
|
|
99
|
+
*Flags* (not to be confused with *Tags*) allow you to set arbitrary key/values, which can be consumed by other parts of your frontend/editor code.
|
|
100
|
+
|
|
101
|
+
You set flags by passing an object, with arbitrary key/values, like so:
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
export const meta: BlockMeta = {
|
|
105
|
+
title: "My Block",
|
|
106
|
+
flags: {
|
|
107
|
+
myFlag: true,
|
|
108
|
+
somethingElse: "green"
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Then, when using `InnerBlocks` or `ContentBlocks`, you can use the flags in `wrapBlock` and `spacer` props — or inside your editor config — to intelligently wrap, style or filter blocks based on these values, rather than by name.
|
|
114
|
+
|
|
115
|
+
Use cases for this system:
|
|
116
|
+
|
|
117
|
+
* Applying class names to the wrapper element in the editor
|
|
118
|
+
* Deciding which blocks to wrap in animation components on the frontend, and which to exclude.
|
|
119
|
+
* Deciding which blocks to wrap in layout classes in a page template
|
|
120
|
+
|
|
121
|
+
Keep flags small. They are copied into the block payload that React receives.
|
|
122
|
+
|
|
123
|
+
## Post Meta Blocks [#post-meta-blocks]
|
|
124
|
+
|
|
125
|
+
Most ACF fields attached to a block are saved against that block instance. `postMetaBlock` changes that relationship: the fields attached to the block are also registered against a WordPress post type.
|
|
126
|
+
|
|
127
|
+
This is useful for template-controlled blocks that represent the main metadata for a post, such as a film header, strand header, team member header, or product summary. It works best when the block is inserted by a template and hidden from the inserter, rather than added randomly by authors.
|
|
128
|
+
|
|
129
|
+
```tsx filename="blocks/strand/strand-header.tsx"
|
|
130
|
+
import { defineBlock, EditableText, usePostExcerptEditor, usePostTitleEditor } from "eddev/blocks"
|
|
131
|
+
|
|
132
|
+
export const meta: BlockMeta = {
|
|
133
|
+
title: "Program Strand",
|
|
134
|
+
icon: "tag",
|
|
135
|
+
inserter: false,
|
|
136
|
+
postMetaBlock: {
|
|
137
|
+
postTypes: ["strand"],
|
|
138
|
+
fieldName: "info",
|
|
139
|
+
},
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default defineBlock("strand/strand-header", (props) => {
|
|
143
|
+
return (
|
|
144
|
+
<header>
|
|
145
|
+
<EditableText as="h1" store={usePostTitleEditor(props.strand?.title!)} />
|
|
146
|
+
<EditableText as="p" store={usePostExcerptEditor(props.strand?.excerpt!)} />
|
|
147
|
+
{props.category?.name && <p>{props.category.name}</p>}
|
|
148
|
+
</header>
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
```graphql filename="blocks/strand/strand-header.graphql"
|
|
154
|
+
query StrandHeader($postId: ID!) {
|
|
155
|
+
block {
|
|
156
|
+
strand_strand_header {
|
|
157
|
+
category {
|
|
158
|
+
name
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
strand(id: $postId, idType: DATABASE_ID) {
|
|
163
|
+
title
|
|
164
|
+
excerpt
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
In this example, ACF fields assigned to `strand/strand-header` can be edited through the block, and are also made available on the `strand` post type under the `info` GraphQL field. eddev also limits the block to one instance per post and applies the configured post types.
|
|
170
|
+
|
|
171
|
+
For this pattern, usually combine:
|
|
172
|
+
|
|
173
|
+
* `postMetaBlock` to attach fields to the post type
|
|
174
|
+
* `inserter: false` so authors do not add extra copies manually
|
|
175
|
+
* an editor template, `headerTemplate`, or `template` so the block is always present
|
|
176
|
+
|
|
177
|
+
## Advanced Fields [#advanced-fields]
|
|
178
|
+
|
|
179
|
+
These are useful, but should not be the first thing you reach for:
|
|
180
|
+
|
|
181
|
+
* `inserter: false{:ts}` hides blocks that are only inserted by templates or code.
|
|
182
|
+
* `lazyLoad: false{:ts}` disables dynamic loading for blocks that are small or almost always visible.
|
|
183
|
+
* `frontendMode: "hidden" | "childrenOnly" | "default"{:ts}` hides utility wrapper blocks or replaces them with their children on the frontend.
|
|
184
|
+
* `templatePart` marks a block as a reusable [template part](./template-parts) such as a header, footer, or sidebar.
|
|
185
|
+
* `cache` and `transforms` exist for specialized cases. Check the current `BlockMeta` type before using them.
|
|
186
|
+
|
|
187
|
+
<Callout type="info">
|
|
188
|
+
Older projects may still use the legacy metadata format. New docs and new blocks should use `export const meta`.
|
|
189
|
+
</Callout>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Core Blocks (/docs/blocks/core-blocks)
|
|
2
|
+
|
|
3
|
+
**Working with WordPress core blocks in eddev**
|
|
4
|
+
|
|
5
|
+
WordPress core blocks are blocks like paragraphs, headings, lists, embeds, and tables. eddev can render them as HTML, tag them for editor rules, or group them into synthetic blocks before React receives them.
|
|
6
|
+
|
|
7
|
+
Keep core block behavior simple. Most sites only need to tag a small set of rich-text blocks and optionally provide React wrappers for their rendered HTML.
|
|
8
|
+
|
|
9
|
+
## Rendering Core Blocks [#rendering-core-blocks]
|
|
10
|
+
|
|
11
|
+
By default, WordPress can render core blocks into `innerHTML`. If a site needs custom React wrappers, add mappings in `blocks/_core.tsx`.
|
|
12
|
+
|
|
13
|
+
```tsx filename="blocks/_core.tsx"
|
|
14
|
+
import { ContentBlock } from "eddev/blocks"
|
|
15
|
+
|
|
16
|
+
const HTMLParagraph = (props: ContentBlock) => {
|
|
17
|
+
if (!props.innerHTML) return null
|
|
18
|
+
return <div className="block-p" dangerouslySetInnerHTML={{ __html: props.innerHTML }} />
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const HTMLList = (props: ContentBlock) => {
|
|
22
|
+
if (!props.innerHTML) return null
|
|
23
|
+
return <div className="block-list" dangerouslySetInnerHTML={{ __html: props.innerHTML }} />
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default {
|
|
27
|
+
"core/paragraph": HTMLParagraph,
|
|
28
|
+
"core/list": HTMLList,
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Use this when the frontend needs consistent wrappers, layout classes, or design-system typography around WordPress-rendered HTML.
|
|
33
|
+
|
|
34
|
+
## Tagging Core Blocks [#tagging-core-blocks]
|
|
35
|
+
|
|
36
|
+
Block tags can be used in `rootBlocks` and `InnerBlocks allowedBlocks`. Your own blocks get tags from `export const meta`; core blocks get tags from PHP.
|
|
37
|
+
|
|
38
|
+
```php filename="backend/blocks.php"
|
|
39
|
+
<?php
|
|
40
|
+
|
|
41
|
+
ED()->tagCoreBlocks("#inline", [
|
|
42
|
+
"core/paragraph",
|
|
43
|
+
"core/list",
|
|
44
|
+
"core/heading",
|
|
45
|
+
]);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
After that, React code can allow all tagged core blocks with one entry:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<InnerBlocks allowedBlocks={["#inline"]} />
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Core blocks can have more than one tag, so it is fine to define broad tags like `#inline` and narrower layout tags for specific templates.
|
|
55
|
+
|
|
56
|
+
## Grouping Core Blocks [#grouping-core-blocks]
|
|
57
|
+
|
|
58
|
+
Some sites group consecutive core blocks into a synthetic block before React receives them.
|
|
59
|
+
|
|
60
|
+
```php filename="backend/blocks.php"
|
|
61
|
+
<?php
|
|
62
|
+
|
|
63
|
+
ED()->groupCoreBlocks("core/rich-text", [
|
|
64
|
+
"core/heading",
|
|
65
|
+
"core/paragraph",
|
|
66
|
+
"core/list",
|
|
67
|
+
"core/table",
|
|
68
|
+
]);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
This converts adjacent matching core blocks into one block with `blockName: "core/rich-text"` and combined `innerHTML`. It is useful when the frontend wants one rich-text wrapper instead of separate heading, paragraph, list, and table blocks.
|
|
72
|
+
|
|
73
|
+
If you group core blocks, add a matching `_core.tsx` renderer:
|
|
74
|
+
|
|
75
|
+
```tsx filename="blocks/_core.tsx"
|
|
76
|
+
import { ContentBlock } from "eddev/blocks"
|
|
77
|
+
|
|
78
|
+
export default {
|
|
79
|
+
"core/rich-text": (props: ContentBlock) => {
|
|
80
|
+
if (!props.innerHTML) return null
|
|
81
|
+
return <div className="wysiwyg" dangerouslySetInnerHTML={{ __html: props.innerHTML }} />
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Only group core blocks when the layout benefits from it. If a site needs individual block-level spacing or wrappers, keep the blocks separate.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
# Data and Editing (/docs/blocks/data-and-editing)
|
|
2
|
+
|
|
3
|
+
**GraphQL props, inline content, and core block rendering**
|
|
4
|
+
|
|
5
|
+
Blocks usually combine three kinds of data:
|
|
6
|
+
|
|
7
|
+
* ACF or WordPress data selected by a paired GraphQL file
|
|
8
|
+
* inline editor values stored directly on the block
|
|
9
|
+
* nested content from `InnerBlocks`
|
|
10
|
+
|
|
11
|
+
Use the smallest tool that fits. Inline text is usually better as `EditableText`. Structured data, relationships, media, or repeated fields are usually better as ACF fields selected through GraphQL.
|
|
12
|
+
|
|
13
|
+
## Paired GraphQL Files [#paired-graphql-files]
|
|
14
|
+
|
|
15
|
+
Place a `.graphql` file beside the block component when the block needs CMS data.
|
|
16
|
+
|
|
17
|
+
```txt filename="blocks/content/image.graphql"
|
|
18
|
+
query {
|
|
19
|
+
block {
|
|
20
|
+
content_image {
|
|
21
|
+
image {
|
|
22
|
+
...ResponsiveImage
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
For a block named `content/image`, eddev exposes its attached ACF fields under `block.content_image`. The field name is generated from the block path by camel-casing each path segment and joining them with an underscore.
|
|
30
|
+
|
|
31
|
+
The query result becomes the component props:
|
|
32
|
+
|
|
33
|
+
```tsx filename="blocks/content/image.tsx"
|
|
34
|
+
import { defineBlock } from "eddev/blocks"
|
|
35
|
+
|
|
36
|
+
export const meta: BlockMeta = {
|
|
37
|
+
title: "Image",
|
|
38
|
+
category: "media",
|
|
39
|
+
tags: ["#root"],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default defineBlock("content/image", (props) => {
|
|
43
|
+
if (!props.image) return null
|
|
44
|
+
return <ResponsiveImage {...props.image} />
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Block queries can also fetch data that is not inside `block { ... }`. For example, a listing block can query posts directly, and a post-specific header block can use `$postId`.
|
|
49
|
+
|
|
50
|
+
```graphql filename="blocks/team/team-listing.graphql"
|
|
51
|
+
query {
|
|
52
|
+
teamMembers(first: 999) {
|
|
53
|
+
nodes {
|
|
54
|
+
title
|
|
55
|
+
uri
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```graphql filename="blocks/film/film-header.graphql"
|
|
62
|
+
query FilmHeader($postId: ID!) {
|
|
63
|
+
film(id: $postId, idType: DATABASE_ID) {
|
|
64
|
+
title
|
|
65
|
+
uri
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Querying Blocks From Views [#querying-blocks-from-views]
|
|
71
|
+
|
|
72
|
+
Views select page blocks with `contentBlocks`.
|
|
73
|
+
|
|
74
|
+
```graphql filename="views/page.graphql"
|
|
75
|
+
query Page($postId: ID!) {
|
|
76
|
+
page(id: $postId, idType: DATABASE_ID) {
|
|
77
|
+
title
|
|
78
|
+
contentBlocks
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Then the view renders them with `ContentBlocks`.
|
|
84
|
+
|
|
85
|
+
```tsx filename="views/page.tsx"
|
|
86
|
+
import { ContentBlocks } from "eddev/blocks"
|
|
87
|
+
import { defineView } from "eddev/views"
|
|
88
|
+
|
|
89
|
+
export default defineView("page", (props) => {
|
|
90
|
+
return <ContentBlocks blocks={props.page?.contentBlocks} />
|
|
91
|
+
})
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`contentBlocks` also accepts a few useful filters:
|
|
95
|
+
|
|
96
|
+
* `include` returns only matching block names, tags, flags, or wildcards.
|
|
97
|
+
* `exclude` removes matching blocks.
|
|
98
|
+
* `flattenExcluded` replaces excluded wrapper blocks with their children.
|
|
99
|
+
* `maxDepth` limits nested block depth.
|
|
100
|
+
* `limit` limits top-level blocks.
|
|
101
|
+
|
|
102
|
+
This is useful when a view needs to render different subsets of the same page content in different layout regions:
|
|
103
|
+
|
|
104
|
+
```graphql filename="views/page.graphql"
|
|
105
|
+
query Page($postId: ID!) {
|
|
106
|
+
page(id: $postId, idType: DATABASE_ID) {
|
|
107
|
+
title
|
|
108
|
+
contentBlocks
|
|
109
|
+
introBlocks: contentBlocks(include: ["content/intro"], limit: 1)
|
|
110
|
+
bodyBlocks: contentBlocks(exclude: ["content/intro"], flattenExcluded: true)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This keeps the data local to the page view. In React, you can render `introBlocks` and `bodyBlocks` in different layout regions without creating a separate runtime query.
|
|
116
|
+
|
|
117
|
+
## Inline Editing [#inline-editing]
|
|
118
|
+
|
|
119
|
+
Use `EditableText` for text authors should edit directly in the preview.
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { defineBlock, EditableText } from "eddev/blocks"
|
|
123
|
+
|
|
124
|
+
export default defineBlock("content/quote", () => {
|
|
125
|
+
return (
|
|
126
|
+
<figure>
|
|
127
|
+
<EditableText as="blockquote" store="quote" placeholder="Enter a quote" />
|
|
128
|
+
<EditableText as="figcaption" store="citation" placeholder="Citation" plainText />
|
|
129
|
+
</figure>
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The important props are:
|
|
135
|
+
|
|
136
|
+
* `store` is required. Pass a string key like `"title"`, or a tuple from `useInlineEditableValue`, `usePostTitleEditor`, `usePostExcerptEditor`, `usePostMetaEditor`, or `useState`.
|
|
137
|
+
* `as` changes the rendered element or component.
|
|
138
|
+
* `placeholder` is editor-only helper text.
|
|
139
|
+
* `defaultValue` is used on the frontend when no value has been entered. It also becomes the editor placeholder when `placeholder` is not provided.
|
|
140
|
+
* `plainText` disables rich-text formatting for labels, buttons, and short UI copy.
|
|
141
|
+
* `disableLineBreaks` prevents multiline editing.
|
|
142
|
+
* `allowedFormats` limits rich-text formats. Use `allowedFormats={[]}` to disable formatting without using `plainText`.
|
|
143
|
+
|
|
144
|
+
`appendOnEnter` and `removeOnDelete` are less common, but useful for text-like block flows. `appendOnEnter` inserts a new paragraph, or a specified block name, when the author presses Enter. `removeOnDelete` removes the current block when the author presses Backspace at the start of the text.
|
|
145
|
+
|
|
146
|
+
## Inline Value Stores [#inline-value-stores]
|
|
147
|
+
|
|
148
|
+
`useInlineEditableValue` reads and writes values in the current block's inline data. It behaves like a scoped `useState`.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
function useInlineEditableValue<T>(
|
|
152
|
+
key: string,
|
|
153
|
+
defaultValue?: T,
|
|
154
|
+
): [value: T | undefined, setValue: (value: T) => void]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
When `defaultValue` is supplied, that value is returned until the author edits the field.
|
|
158
|
+
|
|
159
|
+
Use it when the component needs the current value for rendering, attributes, tracking, conditional UI, or non-text controls.
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { defineBlock, EditableText, useInlineEditableValue } from "eddev/blocks"
|
|
163
|
+
|
|
164
|
+
export default defineBlock("content/cta-button", (props) => {
|
|
165
|
+
const [title] = useInlineEditableValue<string>("title")
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<div data-mixpanel-cta-title={title}>
|
|
169
|
+
<EditableText store="title" defaultValue={props.link?.title} placeholder="Enter a title" />
|
|
170
|
+
</div>
|
|
171
|
+
)
|
|
172
|
+
})
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
You can also pass the tuple directly to `EditableText` so the editable field and the rest of the component share the same value.
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
const titleStore = useInlineEditableValue("title", "Read more")
|
|
179
|
+
const [title] = titleStore
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<a aria-label={title}>
|
|
183
|
+
<EditableText store={titleStore} plainText disableLineBreaks />
|
|
184
|
+
</a>
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The value does not need to be consumed by `EditableText`. For example, an inspector setting can use the same store pattern for booleans or objects.
|
|
189
|
+
|
|
190
|
+
```tsx
|
|
191
|
+
import { InspectorControls, useInlineEditableValue } from "eddev/blocks"
|
|
192
|
+
|
|
193
|
+
function DisplayOptions() {
|
|
194
|
+
const [shown, setShown] = useInlineEditableValue<Record<string, boolean>>("shown", {})
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<>
|
|
198
|
+
{env.admin && (
|
|
199
|
+
<InspectorControls>
|
|
200
|
+
<label>
|
|
201
|
+
<input
|
|
202
|
+
type="checkbox"
|
|
203
|
+
checked={shown.summary ?? true}
|
|
204
|
+
onChange={(event) => setShown({ ...shown, summary: event.currentTarget.checked })}
|
|
205
|
+
/>
|
|
206
|
+
Show summary
|
|
207
|
+
</label>
|
|
208
|
+
</InspectorControls>
|
|
209
|
+
)}
|
|
210
|
+
</>
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
`InspectorControls` is a good place for small per-block controls where ACF would be too heavy. Wrap it in `env.admin` so editor-only controls are not bundled for the frontend.
|
|
216
|
+
|
|
217
|
+
## Core Blocks [#core-blocks]
|
|
218
|
+
|
|
219
|
+
WordPress core blocks can be rendered as HTML, tagged for editor rules, or grouped into synthetic blocks. See [Core Blocks](./core-blocks) for those patterns.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Configuring the Editor (/docs/blocks/editor-config)
|
|
2
|
+
|
|
3
|
+
**Control which blocks authors can use**
|
|
4
|
+
|
|
5
|
+
The editor config lives in `blocks/_editor.tsx`. It decides which blocks are available at the root of a post, which template blocks should be inserted automatically, and how block wrappers should be styled in the WordPress editor.
|
|
6
|
+
|
|
7
|
+
```tsx filename="blocks/_editor.tsx"
|
|
8
|
+
import { defineEditorConfig } from "eddev/blocks"
|
|
9
|
+
|
|
10
|
+
export default defineEditorConfig({
|
|
11
|
+
matchers: [
|
|
12
|
+
{
|
|
13
|
+
match: (post) => post.type === "page" && post.template === "default",
|
|
14
|
+
config: {
|
|
15
|
+
rootBlocks: ["#root"],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Each matcher receives information about the current editor document. Return `true` from `match` when that config should apply.
|
|
23
|
+
|
|
24
|
+
## Root Blocks [#root-blocks]
|
|
25
|
+
|
|
26
|
+
`rootBlocks` controls which blocks can be added at the top level of the post content.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
export default defineEditorConfig({
|
|
30
|
+
matchers: [
|
|
31
|
+
{
|
|
32
|
+
match: (post) => post.type === "page" && post.template === "front-page",
|
|
33
|
+
config: {
|
|
34
|
+
rootBlocks: ["#homepage-root", "content/hero"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Entries can be eddev block names, WordPress core block names, or block tags. The framework default is `["root"]`, but our docs and new examples use explicit `#` tags such as `["#root"]`. Core block tags are configured separately; see [Core Blocks](./core-blocks).
|
|
42
|
+
|
|
43
|
+
## Templates [#templates]
|
|
44
|
+
|
|
45
|
+
Templates let you insert blocks automatically.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
export default defineEditorConfig({
|
|
49
|
+
matchers: [
|
|
50
|
+
{
|
|
51
|
+
match: (post) => post.type === "case-study",
|
|
52
|
+
config: {
|
|
53
|
+
hideTitle: true,
|
|
54
|
+
rootBlocks: ["#root"],
|
|
55
|
+
headerTemplate: [["case-study/header", {}]],
|
|
56
|
+
defaultBlocks: [["core/paragraph", {}]],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
})
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
* `defaultBlocks` are inserted when the post has no editable blocks.
|
|
64
|
+
* `headerTemplate` keeps blocks at the top of the editor.
|
|
65
|
+
* `footerTemplate` keeps blocks at the bottom of the editor.
|
|
66
|
+
* `template` replaces the whole page with a locked block template.
|
|
67
|
+
* `hideTitle` hides the standard WordPress title field when a block edits the title instead.
|
|
68
|
+
|
|
69
|
+
Template entries use eddev block names like `page/page-header`, not ACF names like `acf/page-page-header`.
|
|
70
|
+
|
|
71
|
+
Use `template` when the entire editing surface is fixed. Do not combine it with `headerTemplate` or `footerTemplate`.
|
|
72
|
+
|
|
73
|
+
## Editor Wrapper Classes [#editor-wrapper-classes]
|
|
74
|
+
|
|
75
|
+
Use `generateBlockClassName` to add classes around blocks in the WordPress editor. This does not change frontend markup.
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { BlockWrapperClassGenerator, defineEditorConfig } from "eddev/blocks"
|
|
79
|
+
|
|
80
|
+
const subpageClasses: BlockWrapperClassGenerator = (block) => {
|
|
81
|
+
if (!block.isRootBlock) return
|
|
82
|
+
|
|
83
|
+
if (block.flags?.sideRail) {
|
|
84
|
+
return "admin-subpage-siderail"
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!block.flags?.fullWidth) {
|
|
88
|
+
return "col-subpage-content"
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default defineEditorConfig({
|
|
93
|
+
matchers: [
|
|
94
|
+
{
|
|
95
|
+
match: (post) => post.type === "page" && post.template === "default",
|
|
96
|
+
config: {
|
|
97
|
+
rootBlocks: ["#root", "#inline"],
|
|
98
|
+
generateBlockClassName: subpageClasses,
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
})
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The `block` object includes the block name, slug, tags, flags, parent lookup, root status, and selection/move/remove state. Flags are often the most maintainable way to make layout decisions without checking a long list of block names.
|
|
106
|
+
|
|
107
|
+
## Patterns And Template Parts [#patterns-and-template-parts]
|
|
108
|
+
|
|
109
|
+
Matchers can also target reusable WordPress patterns and template parts:
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
export default defineEditorConfig({
|
|
113
|
+
matchers: [
|
|
114
|
+
{
|
|
115
|
+
match: (post) => post.isPattern,
|
|
116
|
+
config: {
|
|
117
|
+
rootBlocks: ["#inline"],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
match: (post) => post.isTemplatePart && post.slug === "site-header",
|
|
122
|
+
config: {
|
|
123
|
+
rootBlocks: ["parts/part-site-header"],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
})
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
For most projects, keep this limited to shared headers, footers, menus, and content patterns. See [Template Parts](./template-parts) for the current `templatePart` block metadata pattern.
|
|
131
|
+
|
|
132
|
+
## Custom Rich Text Formats [#custom-rich-text-formats]
|
|
133
|
+
|
|
134
|
+
`customRichTextFormats` registers extra formatting buttons for `EditableText`.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
export default defineEditorConfig({
|
|
138
|
+
customRichTextFormats: [
|
|
139
|
+
{
|
|
140
|
+
id: "site/label",
|
|
141
|
+
title: "Label",
|
|
142
|
+
className: "type-label",
|
|
143
|
+
icon: "editor-textcolor",
|
|
144
|
+
tagName: "span",
|
|
145
|
+
disabledByDefault: true,
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
If `disabledByDefault` is true, enable the format on individual fields:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<EditableText store="eyebrow" allowedFormats={["site/label"]} />
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
This is an advanced editor polish tool. Most block text should use the default formatting options, `plainText`, or `allowedFormats={[]}`.
|