@portabletext/editor 7.10.19 → 7.12.0
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/README.md +57 -64
- package/lib/{behavior.types.action-CYtiRDck.d.ts → behavior.types.action-DH6ujvaH.d.ts} +409 -109
- package/lib/behavior.types.action-DH6ujvaH.d.ts.map +1 -0
- package/lib/{behavior.types.behavior-B_77VtQR.js → behavior.types.behavior-BxSLb73I.js} +5 -5
- package/lib/behavior.types.behavior-BxSLb73I.js.map +1 -0
- package/lib/behaviors/index.d.ts +1 -1
- package/lib/behaviors/index.js +1 -1
- package/lib/{get-container-BeXYKpoi.js → get-container-D1nM_ww9.js} +14 -14
- package/lib/get-container-D1nM_ww9.js.map +1 -0
- package/lib/{get-parent-51Kzgaf9.js → get-parent-Ci5241qs.js} +3 -3
- package/lib/get-parent-Ci5241qs.js.map +1 -0
- package/lib/{get-path-sub-schema--2I4NJbG.js → get-path-sub-schema-CZd9VcVM.js} +10 -10
- package/lib/get-path-sub-schema-CZd9VcVM.js.map +1 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +294 -101
- package/lib/index.js.map +1 -1
- package/lib/plugins/index.d.ts +6 -6
- package/lib/plugins/index.js +5 -5
- package/lib/plugins/index.js.map +1 -1
- package/lib/{selector.is-at-the-start-of-block-gJIMWRpU.js → selector.is-at-the-start-of-block-D0boZbDu.js} +4 -4
- package/lib/selector.is-at-the-start-of-block-D0boZbDu.js.map +1 -0
- package/lib/selectors/index.d.ts +6 -6
- package/lib/selectors/index.js +6 -6
- package/lib/selectors/index.js.map +1 -1
- package/lib/traversal/index.d.ts +31 -31
- package/lib/traversal/index.js +4 -4
- package/lib/traversal/index.js.map +1 -1
- package/lib/{util.is-equal-selections-BF4WJtz5.js → util.is-equal-selections-N1hxVUbE.js} +2 -2
- package/lib/{util.is-equal-selections-BF4WJtz5.js.map → util.is-equal-selections-N1hxVUbE.js.map} +1 -1
- package/lib/{util.slice-blocks-DQnr9X2s.js → util.slice-blocks-B2fZcNok.js} +2 -2
- package/lib/{util.slice-blocks-DQnr9X2s.js.map → util.slice-blocks-B2fZcNok.js.map} +1 -1
- package/lib/utils/index.d.ts +1 -1
- package/lib/utils/index.js +3 -3
- package/package.json +3 -3
- package/lib/behavior.types.action-CYtiRDck.d.ts.map +0 -1
- package/lib/behavior.types.behavior-B_77VtQR.js.map +0 -1
- package/lib/get-container-BeXYKpoi.js.map +0 -1
- package/lib/get-parent-51Kzgaf9.js.map +0 -1
- package/lib/get-path-sub-schema--2I4NJbG.js.map +0 -1
- package/lib/selector.is-at-the-start-of-block-gJIMWRpU.js.map +0 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ In order to set up an editor you'll need to:
|
|
|
17
17
|
|
|
18
18
|
- Create a schema that defines the rich text and block content elements.
|
|
19
19
|
- Create a toolbar to toggle and insert these elements.
|
|
20
|
-
-
|
|
20
|
+
- Set up rendering for each element type in the editor, including text blocks and inline formatting.
|
|
21
21
|
- Render the editor.
|
|
22
22
|
|
|
23
23
|
Check out the [Portable Text Playground](../../apps/playground/) for a comprehensive example of the editor in action.
|
|
@@ -41,16 +41,14 @@ Next, in your app or the component you're building, import `EditorProvider`, `Po
|
|
|
41
41
|
```tsx
|
|
42
42
|
// App.tsx
|
|
43
43
|
import {
|
|
44
|
+
defineDecorator,
|
|
44
45
|
defineSchema,
|
|
46
|
+
defineTextBlock,
|
|
45
47
|
EditorProvider,
|
|
46
48
|
PortableTextEditable,
|
|
47
49
|
} from '@portabletext/editor'
|
|
48
|
-
import type {
|
|
49
|
-
|
|
50
|
-
RenderDecoratorFunction,
|
|
51
|
-
RenderStyleFunction,
|
|
52
|
-
} from '@portabletext/editor'
|
|
53
|
-
import {EventListenerPlugin} from '@portabletext/editor/plugins'
|
|
50
|
+
import type {PortableTextBlock} from '@portabletext/editor'
|
|
51
|
+
import {EventListenerPlugin, NodePlugin} from '@portabletext/editor/plugins'
|
|
54
52
|
```
|
|
55
53
|
|
|
56
54
|
### Define the schema
|
|
@@ -103,16 +101,14 @@ Add `useState` from React, then scaffold out a basic application component. For
|
|
|
103
101
|
```tsx
|
|
104
102
|
// app.tsx
|
|
105
103
|
import {
|
|
104
|
+
defineDecorator,
|
|
106
105
|
defineSchema,
|
|
106
|
+
defineTextBlock,
|
|
107
107
|
EditorProvider,
|
|
108
108
|
PortableTextEditable,
|
|
109
109
|
} from '@portabletext/editor'
|
|
110
|
-
import type {
|
|
111
|
-
|
|
112
|
-
RenderDecoratorFunction,
|
|
113
|
-
RenderStyleFunction,
|
|
114
|
-
} from '@portabletext/editor'
|
|
115
|
-
import {EventListenerPlugin} from '@portabletext/editor/plugins'
|
|
110
|
+
import type {PortableTextBlock} from '@portabletext/editor'
|
|
111
|
+
import {EventListenerPlugin, NodePlugin} from '@portabletext/editor/plugins'
|
|
116
112
|
import {useState} from 'react'
|
|
117
113
|
|
|
118
114
|
const schemaDefinition = defineSchema({
|
|
@@ -154,77 +150,77 @@ export default App
|
|
|
154
150
|
|
|
155
151
|
Include the `App` component in your application and run it. You should see an outlined editor that accepts text, but doesn't do much else.
|
|
156
152
|
|
|
157
|
-
###
|
|
153
|
+
### Set up rendering for schema elements
|
|
158
154
|
|
|
159
|
-
At this point the
|
|
155
|
+
At this point the editor renders every text block as plain text, whatever its style. Fix that by registering a `defineTextBlock` node for the text blocks and `defineDecorator`/`defineAnnotation` nodes for the marks. If your editor still renders through the deprecated `renderStyle`, `renderBlock`, `renderListItem`, `renderChild`, `renderDecorator`, and `renderAnnotation` props, see the [migration guide](https://www.portabletext.org/editor/guides/migrate-render-props/) to move to node registrations instead.
|
|
160
156
|
|
|
161
|
-
Start by
|
|
157
|
+
Start by registering the text block render with `defineTextBlock`. The editor dispatches every text block to this callback. Your callback owns the block's wrapper element, so spread `props.attributes` on the outermost element you return, and use the block's `style` to pick the element.
|
|
162
158
|
|
|
163
159
|
```tsx
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
160
|
+
const textBlock = defineTextBlock({
|
|
161
|
+
type: 'block',
|
|
162
|
+
render: (props) => {
|
|
163
|
+
if (props.node.style === 'h1') {
|
|
164
|
+
return <h1 {...props.attributes}>{props.children}</h1>
|
|
165
|
+
}
|
|
166
|
+
if (props.node.style === 'h2') {
|
|
167
|
+
return <h2 {...props.attributes}>{props.children}</h2>
|
|
168
|
+
}
|
|
169
|
+
if (props.node.style === 'h3') {
|
|
170
|
+
return <h3 {...props.attributes}>{props.children}</h3>
|
|
171
|
+
}
|
|
172
|
+
if (props.node.style === 'blockquote') {
|
|
173
|
+
return <blockquote {...props.attributes}>{props.children}</blockquote>
|
|
174
|
+
}
|
|
175
|
+
return <div {...props.attributes}>{props.children}</div>
|
|
176
|
+
},
|
|
177
|
+
})
|
|
179
178
|
```
|
|
180
179
|
|
|
181
|
-
|
|
180
|
+
Marks (decorators and annotations) join the same `nodes` array. Registrations all follow the same shape.
|
|
182
181
|
|
|
183
182
|
- They take in props and return JSX elements.
|
|
184
|
-
- They
|
|
185
|
-
- They return JSX
|
|
183
|
+
- They decide what to render from the registration's `type` and the node itself, not a separate schema-type argument.
|
|
184
|
+
- They return JSX that renders `children` somewhere inside it, the editable content the registration wraps.
|
|
186
185
|
|
|
187
186
|
With this in mind, continue for the remaining schema types.
|
|
188
187
|
|
|
189
|
-
|
|
188
|
+
Register a decorator with `defineDecorator`, one per decorator name.
|
|
190
189
|
|
|
191
190
|
```tsx
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
191
|
+
const strong = defineDecorator({
|
|
192
|
+
type: 'strong',
|
|
193
|
+
render: ({children}) => <strong>{children}</strong>,
|
|
194
|
+
})
|
|
195
|
+
const em = defineDecorator({
|
|
196
|
+
type: 'em',
|
|
197
|
+
render: ({children}) => <em>{children}</em>,
|
|
198
|
+
})
|
|
199
|
+
const underline = defineDecorator({
|
|
200
|
+
type: 'underline',
|
|
201
|
+
render: ({children}) => <u>{children}</u>,
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
const nodes = [textBlock, strong, em, underline]
|
|
204
205
|
```
|
|
205
206
|
|
|
206
207
|
> [!NOTE]
|
|
207
|
-
> By default, text is rendered as an inline `span` element in the editor.
|
|
208
|
-
|
|
209
|
-
Update the `PortableTextEditable` with each corresponding function to attach them to the editor.
|
|
208
|
+
> By default, text is rendered as an inline `span` element in the editor. A decorator's render can pass `children` through unwrapped, but the registered text block render must return a block-level element, like a `<div>`.
|
|
210
209
|
|
|
211
|
-
|
|
210
|
+
Mount every registration through one `NodePlugin`, inside the `EditorProvider`. Keep the `nodes` array itself at module scope, as above: a fresh array on every render would make `NodePlugin` unregister and re-register on every keystroke. You can learn more about [customizing the rendering](https://www.portabletext.org/editor/guides/custom-rendering/) in the documentation.
|
|
212
211
|
|
|
213
212
|
```tsx
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
renderBlock={(props) => <div>{props.children}</div>}
|
|
219
|
-
renderListItem={(props) => <>{props.children}</>}
|
|
220
|
-
/>
|
|
213
|
+
<>
|
|
214
|
+
<NodePlugin nodes={nodes} />
|
|
215
|
+
<PortableTextEditable style={{border: '1px solid black', padding: '0.5em'}} />
|
|
216
|
+
</>
|
|
221
217
|
```
|
|
222
218
|
|
|
223
219
|
Before you can see if anything changed, you need a way to interact with the editor.
|
|
224
220
|
|
|
225
221
|
### Create a toolbar
|
|
226
222
|
|
|
227
|
-
A toolbar is a collection of UI elements for interacting with the editor.
|
|
223
|
+
A toolbar is a collection of UI elements for interacting with the editor. [`@portabletext/toolbar`](../toolbar/) provides ready-made hooks (`useStyleSelector`, `useDecoratorButton`, and more) for common toolbar UI; see the [toolbar customization guide](https://www.portabletext.org/editor/guides/customize-toolbar/) to use them. What follows here is the lower-level approach: sending events to the editor directly.
|
|
228
224
|
|
|
229
225
|
1. Create a `Toolbar` component in the same file.
|
|
230
226
|
2. Import the `useEditor` hook, and declare an `editor` constant in the component.
|
|
@@ -293,7 +289,7 @@ The `useEditor` hook gives you access to the active editor. `send` lets you send
|
|
|
293
289
|
|
|
294
290
|
### Bring it all together
|
|
295
291
|
|
|
296
|
-
With
|
|
292
|
+
With the registrations created and a toolbar in place, you can fully render the editor. Add the `Toolbar` inside the `EditorProvider`.
|
|
297
293
|
|
|
298
294
|
```tsx
|
|
299
295
|
// App.tsx
|
|
@@ -319,12 +315,9 @@ function App() {
|
|
|
319
315
|
}}
|
|
320
316
|
/>
|
|
321
317
|
<Toolbar />
|
|
318
|
+
<NodePlugin nodes={nodes} />
|
|
322
319
|
<PortableTextEditable
|
|
323
320
|
style={{border: '1px solid black', padding: '0.5em'}}
|
|
324
|
-
renderStyle={renderStyle}
|
|
325
|
-
renderDecorator={renderDecorator}
|
|
326
|
-
renderBlock={(props) => <div>{props.children}</div>}
|
|
327
|
-
renderListItem={(props) => <>{props.children}</>}
|
|
328
321
|
/>
|
|
329
322
|
</EditorProvider>
|
|
330
323
|
</>
|