mirror of
https://github.com/resend/react-email.git
synced 2026-09-14 14:18:02 +08:00
139 lines
4.0 KiB
Plaintext
139 lines
4.0 KiB
Plaintext
---
|
|
title: "EditorFocusScope"
|
|
sidebarTitle: "EditorFocusScope"
|
|
description: "Track editor focus across portaled UI with EditorFocusScope."
|
|
icon: "crosshairs"
|
|
---
|
|
|
|
Everything is accessed through a single import:
|
|
|
|
```tsx
|
|
import {
|
|
EditorFocusScope,
|
|
} from '@react-email/editor/ui';
|
|
```
|
|
|
|
The `FocusScopes` extension, included by default in `StarterKit`, keeps the
|
|
editor's focus and blur behavior in sync across registered surfaces. Wrap each
|
|
custom portaled surface that should still count as being inside the editor UI
|
|
with `EditorFocusScope`.
|
|
|
|
<Tip>
|
|
`Inspector.Root`, built-in bubble menus, and slash commands already register
|
|
themselves. Inside custom portaled UI, you usually only add
|
|
`EditorFocusScope` around the portaled content.
|
|
</Tip>
|
|
|
|
<h2 id="editor-focus-scope-provider">EditorFocusScopeProvider</h2>
|
|
|
|
`EditorFocusScopeProvider` is deprecated. Focus tracking moved to the
|
|
`FocusScopes` extension, which is included by default in `StarterKit`, but the
|
|
provider still installs the same tracking plugin for editors that do not use
|
|
`StarterKit`.
|
|
|
|
### Example
|
|
|
|
```tsx
|
|
import { StarterKit } from '@react-email/editor/extensions';
|
|
import { EditorFocusScope } from '@react-email/editor/ui';
|
|
import { EditorContent, EditorContext, useEditor } from '@tiptap/react';
|
|
import * as Popover from '@radix-ui/react-popover';
|
|
|
|
export function MyEditor() {
|
|
const editor = useEditor({
|
|
extensions: [StarterKit.configure()],
|
|
content,
|
|
});
|
|
|
|
return (
|
|
<EditorContext.Provider value={{ editor }}>
|
|
<EditorContent editor={editor} />
|
|
|
|
<Popover.Root>
|
|
<Popover.Trigger type="button">Theme presets</Popover.Trigger>
|
|
|
|
<Popover.Portal>
|
|
<EditorFocusScope>
|
|
<Popover.Content sideOffset={8}>
|
|
<button type="button">Apply preset</button>
|
|
</Popover.Content>
|
|
</EditorFocusScope>
|
|
</Popover.Portal>
|
|
</Popover.Root>
|
|
</EditorContext.Provider>
|
|
);
|
|
}
|
|
```
|
|
|
|
Use `EditorFocusScope` for each portaled surface that should still count as
|
|
being inside the editor UI.
|
|
|
|
### Props
|
|
|
|
<ResponseField name="children" type="ReactNode">
|
|
Rendered unchanged.
|
|
</ResponseField>
|
|
|
|
<ResponseField name="clearSelectionOnBlur" type="boolean" default="true">
|
|
Deprecated. Used only by the provider's fallback plugin. Prefer configuring
|
|
`FocusScopes` in `StarterKit` instead.
|
|
</ResponseField>
|
|
|
|
## EditorFocusScope
|
|
|
|
Use `EditorFocusScope` around portaled UI like Radix `Select.Content`,
|
|
`Popover.Content`, or dialog content so moving focus into that portal is still
|
|
treated as staying inside the editor UI.
|
|
|
|
`EditorFocusScope` registers with the editor's `FocusScopes` extension. If the
|
|
extension is not present, it renders its children unchanged.
|
|
|
|
### Example
|
|
|
|
```tsx
|
|
import { EditorFocusScope, Inspector } from '@react-email/editor/ui';
|
|
import * as Select from '@radix-ui/react-select';
|
|
|
|
<Inspector.Node>
|
|
{({ getAttr, setAttr }) => (
|
|
<Select.Root
|
|
value={String(getAttr('alignment') ?? 'left')}
|
|
onValueChange={(value) => setAttr('alignment', value)}
|
|
>
|
|
<Select.Trigger>
|
|
<Select.Value />
|
|
</Select.Trigger>
|
|
|
|
<Select.Portal>
|
|
<EditorFocusScope>
|
|
<Select.Content>
|
|
<Select.Viewport>
|
|
<Select.Item value="left">
|
|
<Select.ItemText>Left</Select.ItemText>
|
|
</Select.Item>
|
|
<Select.Item value="center">
|
|
<Select.ItemText>Center</Select.ItemText>
|
|
</Select.Item>
|
|
<Select.Item value="right">
|
|
<Select.ItemText>Right</Select.ItemText>
|
|
</Select.Item>
|
|
</Select.Viewport>
|
|
</Select.Content>
|
|
</EditorFocusScope>
|
|
</Select.Portal>
|
|
</Select.Root>
|
|
)}
|
|
</Inspector.Node>
|
|
```
|
|
|
|
If you are building a focus-aware editor shell without `StarterKit`, add the
|
|
`FocusScopes` extension and then use `EditorFocusScope` for each portaled
|
|
surface.
|
|
|
|
### Props
|
|
|
|
<ResponseField name="children" type="ReactNode">
|
|
The element or subtree to register as an additional editor focus scope. Wrap
|
|
the portaled container that receives focus.
|
|
</ResponseField>
|