From e790431b5e3f98173f78fc9efbb700a4b0e7340f Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Thu, 16 Jan 2025 17:29:16 +0800 Subject: [PATCH 1/3] md --- .../components/editor/use-create-editor.ts | 7 +++ .../plate-ui/export-toolbar-button.tsx | 60 ++++++++++--------- .../plate-ui/import-toolbar-button.tsx | 43 +++++++++---- 3 files changed, 71 insertions(+), 39 deletions(-) diff --git a/apps/www/src/registry/default/components/editor/use-create-editor.ts b/apps/www/src/registry/default/components/editor/use-create-editor.ts index 0355dc5608..85cecba88d 100644 --- a/apps/www/src/registry/default/components/editor/use-create-editor.ts +++ b/apps/www/src/registry/default/components/editor/use-create-editor.ts @@ -7,6 +7,7 @@ import { type CreatePlateEditorOptions, ParagraphPlugin, PlateLeaf, + useEditorRef, usePlateEditor, } from '@udecode/plate/react'; import { AIPlugin } from '@udecode/plate-ai/react'; @@ -180,3 +181,9 @@ export const useCreateEditor = ( deps ); }; + +export type MyEditor = ReturnType; + +export const useMyEditor = () => { + return useEditorRef() as MyEditor; +}; diff --git a/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx b/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx index b79645da56..48d1023b03 100644 --- a/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx +++ b/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx @@ -108,6 +108,8 @@ import { TableRowElementStatic } from '@/registry/default/plate-ui/table-row-ele import { TocElementStatic } from '@/registry/default/plate-ui/toc-element-static'; import { ToggleElementStatic } from '@/registry/default/plate-ui/toggle-element-static'; +import type { MyEditor } from '../components/editor/use-create-editor'; + import { DropdownMenu, DropdownMenuContent, @@ -124,7 +126,7 @@ import { ToolbarButton } from './toolbar'; const siteUrl = 'https://platejs.org'; export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { - const editor = useEditorRef(); + const editor = useEditorRef() as MyEditor; const openState = useOpenState(); const getCanvas = async () => { @@ -142,25 +144,21 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { return canvas; }; - const downloadFile = ({ - content, - filename, - isHtml = false, - }: { - content: string; - filename: string; - isHtml?: boolean; - }) => { - const element = document.createElement('a'); - const href = isHtml - ? `data:text/html;charset=utf-8,${encodeURIComponent(content)}` - : content; - element.setAttribute('href', href); - element.setAttribute('download', filename); - element.style.display = 'none'; - document.body.append(element); - element.click(); - element.remove(); + const downloadFile = async (url: string, filename: string) => { + const response = await fetch(url); + + const blob = await response.blob(); + const blobUrl = window.URL.createObjectURL(blob); + + const link = document.createElement('a'); + link.href = blobUrl; + link.download = filename; + document.body.append(link); + link.click(); + link.remove(); + + // Clean up the blob URL + window.URL.revokeObjectURL(blobUrl); }; const exportToPdf = async () => { @@ -179,15 +177,12 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { }); const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true }); - downloadFile({ content: pdfBase64, filename: 'plate.pdf' }); + await downloadFile(pdfBase64, 'plate.pdf'); }; const exportToImage = async () => { const canvas = await getCanvas(); - downloadFile({ - content: canvas.toDataURL('image/png'), - filename: 'plate.png', - }); + await downloadFile(canvas.toDataURL('image/png'), 'plate.png'); }; const exportToHtml = async () => { @@ -361,7 +356,15 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { `; - downloadFile({ content: html, filename: 'plate.html', isHtml: true }); + const url = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; + + await downloadFile(url, 'plate.html'); + }; + + const exportToMarkdown = async () => { + const md = editor.api.markdown.serialize(); + const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`; + await downloadFile(url, 'plate.md'); }; return ( @@ -383,9 +386,8 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { Export as Image - - Export as Markdown{' '} - (coming soon) + + Export as Markdown diff --git a/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx b/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx index bcb6cf3458..90939daf0e 100644 --- a/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx +++ b/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx @@ -9,6 +9,8 @@ import { useEditorRef } from '@udecode/plate/react'; import { ArrowUpToLineIcon } from 'lucide-react'; import { useFilePicker } from 'use-file-picker'; +import type { MyEditor } from '../components/editor/use-create-editor'; + import { DropdownMenu, DropdownMenuContent, @@ -19,22 +21,38 @@ import { } from './dropdown-menu'; import { ToolbarButton } from './toolbar'; +type ImportType = 'html' | 'markdown'; + export function ImportToolbarButton({ children, ...props }: DropdownMenuProps) { - const editor = useEditorRef(); + const editor = useEditorRef() as MyEditor; const openState = useOpenState(); - const { openFilePicker } = useFilePicker({ - accept: ['text/html'], - multiple: false, - onFilesSelected: async ({ plainFiles }) => { - const text = await plainFiles[0].text(); + const [type, setType] = React.useState('html'); + const accept = type === 'html' ? ['text/html'] : ['.md']; + const getFileNodes = (text: string, type: ImportType) => { + if (type === 'html') { const editorNode = getEditorDOMFromHtmlString(text); - const nodes = editor.api.html.deserialize({ element: editorNode, }); + return nodes; + } + + const nodes = editor.api.markdown.deserialize(text); + + return nodes; + }; + + const { openFilePicker } = useFilePicker({ + accept, + multiple: false, + onFilesSelected: async ({ plainFiles }) => { + const text = await plainFiles[0].text(); + + const nodes = getFileNodes(text, type); + editor.tf.insertNodes(nodes); }, }); @@ -51,15 +69,20 @@ export function ImportToolbarButton({ children, ...props }: DropdownMenuProps) { { + setType('html'); openFilePicker(); }} > Import from HTML - - Import from Markdown{' '} - (coming soon) + { + setType('markdown'); + openFilePicker(); + }} + > + Import from Markdown From aee2fdeb518f64462541096fc9f154526b2fd0da Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Thu, 16 Jan 2025 18:26:17 +0800 Subject: [PATCH 2/3] fix --- .../default/components/editor/use-create-editor.ts | 7 ------- .../registry/default/plate-ui/export-toolbar-button.tsx | 7 +++---- .../registry/default/plate-ui/import-toolbar-button.tsx | 7 +++---- 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/apps/www/src/registry/default/components/editor/use-create-editor.ts b/apps/www/src/registry/default/components/editor/use-create-editor.ts index 85cecba88d..0355dc5608 100644 --- a/apps/www/src/registry/default/components/editor/use-create-editor.ts +++ b/apps/www/src/registry/default/components/editor/use-create-editor.ts @@ -7,7 +7,6 @@ import { type CreatePlateEditorOptions, ParagraphPlugin, PlateLeaf, - useEditorRef, usePlateEditor, } from '@udecode/plate/react'; import { AIPlugin } from '@udecode/plate-ai/react'; @@ -181,9 +180,3 @@ export const useCreateEditor = ( deps ); }; - -export type MyEditor = ReturnType; - -export const useMyEditor = () => { - return useEditorRef() as MyEditor; -}; diff --git a/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx b/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx index 48d1023b03..75cda0bd1b 100644 --- a/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx +++ b/apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx @@ -49,6 +49,7 @@ import { BaseKbdPlugin } from '@udecode/plate-kbd'; import { BaseColumnItemPlugin, BaseColumnPlugin } from '@udecode/plate-layout'; import { BaseLineHeightPlugin } from '@udecode/plate-line-height'; import { BaseLinkPlugin } from '@udecode/plate-link'; +import { MarkdownPlugin } from '@udecode/plate-markdown'; import { BaseEquationPlugin, BaseInlineEquationPlugin, @@ -108,8 +109,6 @@ import { TableRowElementStatic } from '@/registry/default/plate-ui/table-row-ele import { TocElementStatic } from '@/registry/default/plate-ui/toc-element-static'; import { ToggleElementStatic } from '@/registry/default/plate-ui/toggle-element-static'; -import type { MyEditor } from '../components/editor/use-create-editor'; - import { DropdownMenu, DropdownMenuContent, @@ -126,7 +125,7 @@ import { ToolbarButton } from './toolbar'; const siteUrl = 'https://platejs.org'; export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { - const editor = useEditorRef() as MyEditor; + const editor = useEditorRef(); const openState = useOpenState(); const getCanvas = async () => { @@ -362,7 +361,7 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { }; const exportToMarkdown = async () => { - const md = editor.api.markdown.serialize(); + const md = editor.getApi(MarkdownPlugin).markdown.serialize(); const url = `data:text/markdown;charset=utf-8,${encodeURIComponent(md)}`; await downloadFile(url, 'plate.md'); }; diff --git a/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx b/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx index 90939daf0e..c5bb061cbc 100644 --- a/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx +++ b/apps/www/src/registry/default/plate-ui/import-toolbar-button.tsx @@ -6,11 +6,10 @@ import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; import { getEditorDOMFromHtmlString } from '@udecode/plate'; import { useEditorRef } from '@udecode/plate/react'; +import { MarkdownPlugin } from '@udecode/plate-markdown'; import { ArrowUpToLineIcon } from 'lucide-react'; import { useFilePicker } from 'use-file-picker'; -import type { MyEditor } from '../components/editor/use-create-editor'; - import { DropdownMenu, DropdownMenuContent, @@ -24,7 +23,7 @@ import { ToolbarButton } from './toolbar'; type ImportType = 'html' | 'markdown'; export function ImportToolbarButton({ children, ...props }: DropdownMenuProps) { - const editor = useEditorRef() as MyEditor; + const editor = useEditorRef(); const openState = useOpenState(); const [type, setType] = React.useState('html'); @@ -40,7 +39,7 @@ export function ImportToolbarButton({ children, ...props }: DropdownMenuProps) { return nodes; } - const nodes = editor.api.markdown.deserialize(text); + const nodes = editor.getApi(MarkdownPlugin).markdown.deserialize(text); return nodes; }; From d3a147ff49329f7ee3bd6070387b947d369b0463 Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Thu, 16 Jan 2025 19:23:50 +0800 Subject: [PATCH 3/3] docs --- apps/www/content/docs/en/components/changelog.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/www/content/docs/en/components/changelog.mdx b/apps/www/content/docs/en/components/changelog.mdx index dd70518f91..acc47df776 100644 --- a/apps/www/content/docs/en/components/changelog.mdx +++ b/apps/www/content/docs/en/components/changelog.mdx @@ -10,8 +10,11 @@ Use the [CLI](https://platejs.org/docs/components/cli) to install the latest ver ## January 2025 #18 +### January 16 #18.4 +- `import-toolbar-button` and `export-toolbar-button`: add `markdown` support + ### January 14 #18.3 -- `import-toolbar-button`: add `import-toolbar-button` +- `fixed-toolbar-buttons`: add `import-toolbar-button` - `indent-fire-marker.tsx` Add `data-plate-prevent-deserialization` to prevent deserialization of the fire marker. Change the `span` tag to `li`. - `indent-todo-marker.tsx` change the `span` tag to `li`. - `image-element-static.tsx` and `hr-element-static.tsx`: Fix `nodeProps` not being passed to `SlateElement`.