Skip to content

Commit bf62831

Browse files
authored
Merge pull request #4431 from udecode/staitc/copy
set x-slate-fragment in PlateView
2 parents ac25821 + 1863d89 commit bf62831

34 files changed

+1882
-76
lines changed

.changeset/platejs-core-minor.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
"@platejs/core": minor
3+
---
4+
5+
Added comprehensive copy functionality and view editor support for static rendering.
6+
7+
**New Components:**
8+
- Added `PlateView` component for static editor rendering with copy support
9+
- Added `usePlateViewEditor` hook for creating memoized static editors
10+
11+
**Static Editor Enhancements:**
12+
- Added `withStatic` HOC to enhance editors with static rendering capabilities
13+
- Added `ViewPlugin` that enables copy operations in static editors
14+
- Added `getStaticPlugins` to configure plugins for static rendering
15+
- Added `onCopy` handler that properly serializes content with `x-slate-fragment` format
16+
17+
**New Utilities:**
18+
- Added `getSelectedDomBlocks` to extract selected DOM elements with Slate metadata
19+
- Added `getSelectedDomNode` to get DOM nodes from browser selection
20+
- Added `isSelectOutside` to check if selection is outside editor bounds
21+
- Added `getPlainText` to recursively extract plain text from DOM nodes
22+
23+
This enables seamless copy operations from static Plate editors, allowing content to be pasted into other Slate editors while preserving rich formatting and structure.

apps/www/src/app/dev/layout.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default function DevLayout(props: { children: React.ReactNode }) {
2+
return (
3+
<>
4+
<main>{props.children}</main>
5+
</>
6+
);
7+
}

apps/www/src/app/dev/page.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
'use client';
2+
3+
import { useEffect, useState } from 'react';
4+
5+
import { usePlateViewEditor } from '@platejs/core/react';
6+
7+
import { BaseEditorKit } from '@/registry/components/editor/editor-base-kit';
8+
import { playgroundValue } from '@/registry/examples/values/playground-value';
9+
import { EditorView } from '@/registry/ui/editor';
10+
import { EditorStatic } from '@/registry/ui/editor-static';
11+
12+
export default function DevPage() {
13+
const [isClient, setIsClient] = useState(false);
14+
15+
useEffect(() => {
16+
setIsClient(true);
17+
}, []);
18+
19+
// const editor = createStaticEditor({ plugins: BaseEditorKit });
20+
const editor = usePlateViewEditor(
21+
{
22+
plugins: BaseEditorKit,
23+
value: playgroundValue,
24+
},
25+
[]
26+
);
27+
28+
if (!isClient) {
29+
return <main>Loading...</main>;
30+
}
31+
32+
return (
33+
<main>
34+
<EditorView editor={editor} />
35+
36+
<h1>123</h1>
37+
38+
<EditorStatic editor={editor} />
39+
</main>
40+
);
41+
}

apps/www/src/registry/ui/code-block-node-static.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function CodeBlockElementStatic(
1313
) {
1414
return (
1515
<SlateElement
16-
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] **:[.hljs-built\\_in,.hljs-symbol]:text-[#e36209] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#d73a49] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#6f42c1] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] dark:**:[.hljs-built\\_in,.hljs-symbol]:text-[#c3854e] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#ee6960] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] dark:**:[.hljs-section]:text-[#61a5f2] dark:**:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#a77bfa]"
16+
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] **:[.hljs-built\\\\_in,.hljs-symbol]:text-[#e36209] dark:**:[.hljs-built\\\\_in,.hljs-symbol]:text-[#c3854e] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#d73a49] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#ee6960] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] dark:**:[.hljs-section]:text-[#61a5f2] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#6f42c1] dark:**:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#a77bfa]"
1717
{...props}
1818
>
1919
<div className="relative rounded-md bg-muted/50">

apps/www/src/registry/ui/code-block-node.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function CodeBlockElement(props: PlateElementProps<TCodeBlockElement>) {
3434

3535
return (
3636
<PlateElement
37-
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] **:[.hljs-built\\_in,.hljs-symbol]:text-[#e36209] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#d73a49] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#6f42c1] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] dark:**:[.hljs-built\\_in,.hljs-symbol]:text-[#c3854e] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\_]:text-[#ee6960] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] dark:**:[.hljs-section]:text-[#61a5f2] dark:**:[.hljs-title,.hljs-title.class\\_,.hljs-title.class\\_.inherited\\_\\_,.hljs-title.function\\_]:text-[#a77bfa]"
37+
className="py-1 **:[.hljs-addition]:bg-[#f0fff4] **:[.hljs-addition]:text-[#22863a] dark:**:[.hljs-addition]:bg-[#3c5743] dark:**:[.hljs-addition]:text-[#ceead5] **:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#005cc5] dark:**:[.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable]:text-[#6596cf] **:[.hljs-built\\\\_in,.hljs-symbol]:text-[#e36209] dark:**:[.hljs-built\\\\_in,.hljs-symbol]:text-[#c3854e] **:[.hljs-bullet]:text-[#735c0f] **:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] dark:**:[.hljs-comment,.hljs-code,.hljs-formula]:text-[#6a737d] **:[.hljs-deletion]:bg-[#ffeef0] **:[.hljs-deletion]:text-[#b31d28] dark:**:[.hljs-deletion]:bg-[#473235] dark:**:[.hljs-deletion]:text-[#e7c7cb] **:[.hljs-emphasis]:italic **:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#d73a49] dark:**:[.hljs-keyword,.hljs-doctag,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language\\\\_]:text-[#ee6960] **:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#22863a] dark:**:[.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo]:text-[#36a84f] **:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#032f62] dark:**:[.hljs-regexp,.hljs-string,.hljs-meta_.hljs-string]:text-[#3593ff] **:[.hljs-section]:font-bold **:[.hljs-section]:text-[#005cc5] dark:**:[.hljs-section]:text-[#61a5f2] **:[.hljs-strong]:font-bold **:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#6f42c1] dark:**:[.hljs-title,.hljs-title.class\\\\_,.hljs-title.class\\\\_.inherited\\\\_\\\\_,.hljs-title.function\\\\_]:text-[#a77bfa]"
3838
{...props}
3939
>
4040
<div className="relative rounded-md bg-muted/50">

apps/www/src/registry/ui/editor.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
import * as React from 'react';
44

55
import type { VariantProps } from 'class-variance-authority';
6-
import type { PlateContentProps } from 'platejs/react';
6+
import type { PlateContentProps, PlateViewProps } from 'platejs/react';
77

88
import { cva } from 'class-variance-authority';
9-
import { PlateContainer, PlateContent } from 'platejs/react';
9+
import { PlateContainer, PlateContent, PlateView } from 'platejs/react';
1010

1111
import { cn } from '@/lib/utils';
1212

@@ -112,3 +112,18 @@ export const Editor = React.forwardRef<HTMLDivElement, EditorProps>(
112112
);
113113

114114
Editor.displayName = 'Editor';
115+
116+
export function EditorView({
117+
className,
118+
variant,
119+
...props
120+
}: PlateViewProps & VariantProps<typeof editorVariants>) {
121+
return (
122+
<PlateView
123+
{...props}
124+
className={cn(editorVariants({ variant }), className)}
125+
/>
126+
);
127+
}
128+
129+
EditorView.displayName = 'EditorView';

docs/components/changelog.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ Since Plate UI is not a component library, a changelog is maintained here.
88

99
Use the [CLI](https://platejs.org/docs/components/cli) to install the latest version of the components.
1010

11+
## July 2025 #24
12+
13+
### July 2 #24.1
14+
- `editor`: Added `EditorView` component using the new `PlateView` from `@platejs/core/react` for static editor rendering with copy functionality
15+
1116
## June 2025 #23
1217

1318
### June 29 #23.9

packages/ai/src/lib/streaming/streamDeserializeMd.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,10 @@ export const streamDeserializeMd = (
1515

1616
let blocks = [];
1717

18-
blocks = editor
19-
.getApi(MarkdownPlugin)
20-
.markdown.deserialize(input, {
21-
...options,
22-
preserveEmptyParagraphs: false,
23-
});
18+
blocks = editor.getApi(MarkdownPlugin).markdown.deserialize(input, {
19+
...options,
20+
preserveEmptyParagraphs: false,
21+
});
2422

2523
const trimmedData = getChunkTrimmed(data);
2624

packages/core/src/internal/plugin/resolvePlugins.spec.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { SlateEditor } from '../../lib/editor';
22

3-
import { createSlatePlugin } from '../../lib/plugin';
4-
import { DebugPlugin } from '../../lib/plugins';
5-
import { createPlateEditor } from '../../react';
3+
import { createSlatePlugin } from '../../lib/plugin/createSlatePlugin';
4+
import { DebugPlugin } from '../../lib/plugins/debug/DebugPlugin';
5+
import { createPlateEditor } from '../../react/editor/withPlate';
66
import { createPlatePlugin } from '../../react/plugin/createPlatePlugin';
77
import { getPlugin } from '../../react/plugin/getPlugin';
88
import { resolvePluginTest } from './resolveCreatePluginTest';
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/**
2+
* @file Automatically generated by barrelsby.
3+
*/
4+
5+
export * from './withStatic';

0 commit comments

Comments
 (0)