Skip to content

Commit

Permalink
Merge pull request #2629 from udecode/plate-24
Browse files Browse the repository at this point in the history
Plate 24
  • Loading branch information
zbeyens authored Sep 17, 2023
2 parents c216041 + 30c1546 commit 5c2915d
Show file tree
Hide file tree
Showing 213 changed files with 1,659 additions and 1,587 deletions.
39 changes: 39 additions & 0 deletions .changeset/core-major.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
'@udecode/plate-core': major
---

- [**Breaking**] Rename `Plate` to `PlateContent`.
- [**Breaking**] Rename `PlateProvider` to `Plate`.
- [**Breaking**] Rendering `PlateContent` is now required in `Plate`. This allows you to choose where to render the editor next to other components like toolbar. Example:

```tsx
// Before
<Plate />
// or
<PlateProvider>
<Plate />
</PlateProvider>

// After
<Plate>
<PlateContent />
</Plate>
```

- [**Breaking**] Remove provider props such as `plugins` from `PlateContent`. These props should be passed to `Plate`.
- [**Breaking**] Remove `editableProps` prop from `PlateContent`. Move these as`PlateContent` props.
- [**Breaking**] Remove `children` prop from `PlateContent`. Render instead these components after `PlateContent`.
- [**Breaking**] Remove `firstChildren` prop from `PlateContent`. Render instead these components before `PlateContent`.
- [**Breaking**] Remove `editableRef` prop from `PlateContent`. Use `ref` instead.
- [**Breaking**] Remove `withPlateProvider`.
- [**Breaking**] Rename `usePlateEditorRef` to `useEditorRef`.
- [**Breaking**] Rename `usePlateEditorState` to `useEditorState`.
- [**Breaking**] Rename `usePlateReadOnly` to `useEditorReadOnly`. This hook can be used below `Plate` while `useReadOnly` can only be used in node components.
- [**Breaking**] Rename `usePlateSelection` to `useEditorSelection`.
- [**Breaking**] Rename store attributes `keyDecorate`, `keyEditor` and `keySelection` to `versionDecorate`, `versionEditor` and `versionSelection`. These are now numbers incremented on each change.
- [**Breaking**] Rename store attribute `isRendered` to `isMounted`.
- Add `maxLength` prop to `Plate`. Specifies the maximum number of characters allowed in the editor. This is a new core plugin (`createLengthPlugin`).
- Add `useEditorVersion` hook. Version incremented on each editor change.
- Add `useSelectionVersion` hook. Version incremented on each selection change.
- Fix `editor.reset` should now reset the editor without mutating the ref so it does not remount `PlateContent`. Default is using `resetEditor`. If you need to replace the editor ref, use `useReplaceEditor`.
- [Type] Remove generic from `TEditableProps`, `RenderElementFn`, `RenderAfterEditable`
5 changes: 5 additions & 0 deletions .changeset/slate-react.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/slate-react': minor
---

- Add `focusEditorEdge` transform. Focus the editor at the start or end of the document.
5 changes: 5 additions & 0 deletions .changeset/slate-utils.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@udecode/slate-utils': minor
---

- Add `isEditorEmpty` query. Whether the editor is empty. An editor is empty if it has only one empty element.
15 changes: 9 additions & 6 deletions apps/e2e-examples/src/TableApp.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { createPlateUI } from '@/plate/create-plate-ui';
import { createPlugins, createTablePlugin, Plate } from '@udecode/plate';
import {
createPlugins,
createTablePlugin,
Plate,
PlateContent,
} from '@udecode/plate';

import { useVariant } from './useVariant';

Expand Down Expand Up @@ -80,10 +85,8 @@ export function TableApp() {
];

return (
<Plate
plugins={plugins}
initialValue={initialValue}
editableProps={{ readOnly }}
/>
<Plate plugins={plugins} initialValue={initialValue}>
<PlateContent readOnly={readOnly} />
</Plate>
);
}
54 changes: 33 additions & 21 deletions apps/www/content/docs/accessing-editor.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -57,19 +57,19 @@ const createMyPlugin = createPluginFactory({

## From a Child of Plate

Use the **`usePlateEditorRef`** or **`usePlateEditorState`** hooks.
Use the **`useEditorRef`** or **`useEditorState`** hooks.

Internally, **`usePlateEditorState`** is a wrapper for **`usePlateEditorRef`**. The only difference is that **`usePlateEditorState`** causes React to re-render whenever the **`editor`** state changes, whereas **`usePlateEditorRef`** does not cause a re-render. Since **`editor`** is mutable and is updated by reference, **`usePlateEditorRef`** will be sufficient (and more efficient) in most situations.
Internally, **`useEditorState`** is a wrapper for **`useEditorRef`**. The only difference is that **`useEditorState`** causes React to re-render whenever the **`editor`** state changes, whereas **`useEditorRef`** does not cause a re-render. Since **`editor`** is mutable and is updated by reference, **`useEditorRef`** will be sufficient (and more efficient) in most situations.

You can call these hooks from any React component that is rendered as a descendant of the **`Plate`** (or **`PlateProvider`**) component, including [Plugin Components](/docs/plugin-components).
You can call these hooks from any React component that is rendered as a descendant of the **`Plate`** component, including [Plugin Components](/docs/plugin-components).

```tsx showLineNumbers {6}
const ParagraphElement = ({
className,
children,
...props
}: PlateElementProps) => {
const editor = usePlateEditorRef();
const editor = useEditorRef();

const handleClick = useCallback(() => {
console.info('You clicked on a paragraph, and the editor is ', editor);
Expand All @@ -87,7 +87,7 @@ One common pattern is to add an effect component as a child of **`Plate`** that

```tsx showLineNumbers {2,17}
const CustomEffect = () => {
const editor = usePlateEditorRef();
const editor = useEditorRef();

useEffect(() => {
const interval = setInterval(() => {
Expand All @@ -103,6 +103,8 @@ const CustomEffect = () => {
export default () => (
<Plate>
<CustomEffect />

<PlateContent />
</Plate>
);
```
Expand All @@ -118,28 +120,26 @@ export default () => (

## From a Sibling of Plate

Wrap **`Plate`** and the sibling in **`PlateProvider`**, and then use **`usePlateEditorRef`** or **`usePlateEditorState`** from within the sibling.

Note that certain **`Plate`** props, such as **`plugins`** and **`initialValue`**, must be lifted up to the **`PlateProvider`**. See [From an Ancestor](#from-an-ancestor) if this is impractical or impossible for you to achieve.
Wrap **`PlateContent`** and the sibling in **`Plate`**, and then use **`useEditorRef`** or **`useEditorState`** from within the sibling.

```tsx showLineNumbers {2,8,11}
const Toolbar = () => {
const editor = usePlateEditorState();
const editor = useEditorState();
// Do something with editor
// ...
};

export default () => (
<PlateProvider>
const Editor = () => (
<Plate>
<Toolbar />
<Plate />
</PlateProvider>
<PlateContent />
</Plate>
);
```

## From an Ancestor

If you need to access the **`editor`** instance from an ancestor of **`Plate`**, wrapping the relevant components in a **`PlateProvider`** is the preferred solution. If this is not an option, you can instead use the **`editorRef`** prop to pass a reference to the **`editor`** instance up the React component tree to where it is needed.
If you need to access the **`editor`** instance from an ancestor of **`PlateContent`**, wrapping the relevant components in a **`Plate`** is the preferred solution. If this is not an option, you can instead use the **`editorRef`** prop to pass a reference to the **`editor`** instance up the React component tree to where it is needed.

The **`editorRef`** prop can be used with **`useRef`**, **`useState`**, or a custom ref callback. Regardless of which you use, you'll need to handle the case where **`editor`** is null. This happens when the editor hasn't had a chance to render yet or has unmounted.

Expand All @@ -162,18 +162,22 @@ const App = () => {
// ...
};

const MyEditor = ({
const Editor = ({
editorRef,
}: {
editorRef: MutableRefObject<PlateEditor | null>;
}) => <Plate editorRef={editorRef} />;
}) => (
<Plate editorRef={editorRef}>
<PlateContent />
</Plate>
);
```

### With State

If you want your ancestor component to re-render when the editor content changes, you may want to use **`useState`** to store your **`editor`** instance. Since the **`editorRef`** callback is only called once when the editor first mounts, you'll also need to manually trigger a re-render by updating a counter whenever the **`onChange`** handler of **`Plate`** is called.

Using **`editorRef`** with **`useState`** without a counter is equivalent to using **`usePlateEditorRef`** instead of **`usePlateEditorState`** (the difference is discussed above). Most of the time, if you don't need the ancestor component to re-render on every change, you should be using **`useRef`** instead.
Using **`editorRef`** with **`useState`** without a counter is equivalent to using **`useEditorRef`** instead of **`useEditorState`** (the difference is discussed above). Most of the time, if you don't need the ancestor component to re-render on every change, you should be using **`useRef`** instead.

```tsx showLineNumbers {2-3,34-35}
const App = () => {
Expand All @@ -199,13 +203,17 @@ const EditorPreview = ({ editor }: { editor: PlateEditor | null }) => {
return <div dangerouslySetInnerHTML={{ __html: html }} />;
};

const MyEditor = ({
const Editor = ({
setEditor,
handleUpdateEditor,
}: {
setEditor: (editor: PlateEditor | null) => void;
handleUpdateEditor: () => void;
}) => <Plate editorRef={setEditor} onChange={handleUpdateEditor} />;
}) => (
<Plate editorRef={setEditor} onChange={handleUpdateEditor}>
<PlateContent />
</Plate>
);
```

## Temporary Editor Instance
Expand All @@ -222,7 +230,7 @@ const plugins = createPlugins([
// ...
]);

export default ({ initialHtml }: { initialHtml: string }) => {
const Editor = ({ initialHtml }: { initialHtml: string }) => {
/**
* Changing the initialValue after render is not supported, so initialHtml
* is not included in the useMemo deps.
Expand All @@ -234,6 +242,10 @@ export default ({ initialHtml }: { initialHtml: string }) => {
});
}, []);

return <Plate plugins={plugins} initialValue={initialValue} />;
return (
<Plate plugins={plugins} initialValue={initialValue}>
<PlateContent />
</Plate>
);
};
```
67 changes: 33 additions & 34 deletions apps/www/content/docs/api/core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ Hook to get the element from a node component.
<APIItem name="pluginKey" type="string" optional>
The plugin key.

- **Default:** `SCOPE_ELEMENT`.
- **Default:** the closest element key.

</APIItem>
</APIParameters>
Expand All @@ -327,12 +327,17 @@ The value of the element as a TElement.

### useEditorRef

Get the Slate editor reference.
Get the Slate editor reference without re-rendering.

- Does not re-render on editor change.
- Does not support nested editors.
- Should be used inside `Plate`.
- Note the reference does not change when the editor changes.

<APIParameters>
<APIItem name="id" type="PlateId" optional>
The ID of the plate editor. Useful only when nesting editors. Default is using the closest editor id.
</APIItem>
</APIParameters>
<APIReturns>

A `PlateEditor` object, which is the Slate editor.
Expand All @@ -341,81 +346,71 @@ A `PlateEditor` object, which is the Slate editor.

### useEditorState

Get the Slate editor reference.
Get the Slate editor reference with re-rendering.

- Re-renders on editor change.
- Does not support nested editors.
- Supports nested editors.
- Should be used inside `Plate`.
- Note the reference does not change when the editor changes.

<APIParameters>
<APIItem name="id" type="PlateId" optional>
The ID of the plate editor. Default is using the closest editor id.
</APIItem>
</APIParameters>
<APIReturns>

A `PlateEditor` object, which is the Slate editor.

</APIReturns>

### usePlateEditorRef
### useEditorReadOnly

Get an editor reference which is never updated.

- Does not re-render on editor change.
- Supports nested editors.
- Should be used inside `Plate` or `PlateProvider`.
Get the editor's `readOnly` state.

<APIParameters>
<APIItem name="id" type="PlateId" optional>
The ID of the plate editor. Useful only when nesting editors.
The ID of the plate editor.
</APIItem>
</APIParameters>
<APIReturns>

A `PlateEditor` object, which is the Slate editor.
The `readOnly` state of the editor.

</APIReturns>

### usePlateEditorState

Get the Slate editor reference. Re-renders on editor change.
### useEditorSelection

- Re-renders on editor change.
- Supports nested editors.
- Should be used inside `Plate` or `PlateProvider`.
Get the editor's selection. Memoized so it does not re-render if the range is the same.

<APIParameters>
<APIItem name="id" type="PlateId" optional>
The ID of the plate editor.
</APIItem>
</APIParameters>
<APIReturns>

A `PlateEditor` object, which is the Slate editor.

</APIReturns>
<APIReturns>The current selection in the editor.</APIReturns>

### usePlateReadOnly
### useEditorVersion

Get the editor's `readOnly` state.
Get the version of the editor value. That version is incremented on each editor change.

<APIParameters>
<APIItem name="id" type="PlateId" optional>
The ID of the plate editor.
</APIItem>
</APIParameters>
<APIReturns>

The `readOnly` state of the editor.

</APIReturns>
<APIReturns>The current version of the editor value.</APIReturns>

### usePlateSelection
### useSelectionVersion

Get the editor's selection which is updated on each editor change.
Get the version of the editor selection. That version is incremented on each selection change (the range being different).

<APIParameters>
<APIItem name="id" type="PlateId" optional>
The ID of the plate editor.
</APIItem>
</APIParameters>
<APIReturns>The current selection in the editor.</APIReturns>
<APIReturns>The current version of the editor selection.</APIReturns>

## Core plugins

Expand Down Expand Up @@ -447,6 +442,10 @@ Creates a plugin that merges and registers all the inline types and void types f

Creates a plugin that overrides the default `insertData` function in the editor, which is called when data is being pasted or dragged into the editor.

### createLengthPlugin

Creates a plugin that enforces a maximum length for the editor.

### createNodeFactoryPlugin

Creates a plugin that overrides the default `blockFactory` and `childrenFactory` functions in the editor. These functions are used to generate the default blocks and children for the editor.
Expand Down
Loading

0 comments on commit 5c2915d

Please sign in to comment.