From 6a864cb29ee8751e7d6a0cd75e2360bd51fd4348 Mon Sep 17 00:00:00 2001 From: Felix Feng Date: Tue, 26 Nov 2024 21:18:17 +0800 Subject: [PATCH] sync --- apps/www/public/r/index.json | 3 +- apps/www/public/r/styles/default/editor.json | 2 +- .../styles/default/export-toolbar-button.json | 2 +- .../styles/default/fixed-toolbar-buttons.json | 5 +- apps/www/src/__registry__/index.tsx | 2 +- .../plate-playground-template/package.json | 2 + .../plate-playground-template/pnpm-lock.yaml | 76 +++++++++++++++ .../src/components/plate-ui/button.tsx | 2 +- .../src/components/plate-ui/editor.tsx | 5 +- .../plate-ui/export-toolbar-button.tsx | 93 +++++++++++++++++++ .../plate-ui/fixed-toolbar-buttons.tsx | 8 ++ .../src/components/plate-ui/input.tsx | 2 +- .../plate-ui/media-audio-element.tsx | 1 - .../src/components/plate-ui/toc-element.tsx | 4 +- .../src/components/plate-ui/toolbar.tsx | 1 - 15 files changed, 193 insertions(+), 15 deletions(-) create mode 100644 templates/plate-playground-template/src/components/plate-ui/export-toolbar-button.tsx diff --git a/apps/www/public/r/index.json b/apps/www/public/r/index.json index aec680ff36..3315701857 100644 --- a/apps/www/public/r/index.json +++ b/apps/www/public/r/index.json @@ -1975,7 +1975,8 @@ "outdent-toolbar-button", "table-dropdown-menu", "toggle-toolbar-button", - "turn-into-dropdown-menu" + "turn-into-dropdown-menu", + "export-toolbar-button" ], "type": "registry:ui" }, diff --git a/apps/www/public/r/styles/default/editor.json b/apps/www/public/r/styles/default/editor.json index 8f18e6108c..0df7274217 100644 --- a/apps/www/public/r/styles/default/editor.json +++ b/apps/www/public/r/styles/default/editor.json @@ -15,7 +15,7 @@ }, "files": [ { - "content": "'use client';\n\nimport React from 'react';\n\nimport type { PlateContentProps } from '@udecode/plate-common/react';\nimport type { VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@udecode/cn';\nimport {\n PlateContent,\n useEditorContainerRef,\n useEditorRef,\n} from '@udecode/plate-common/react';\nimport { cva } from 'class-variance-authority';\n\nconst editorContainerVariants = cva(\n 'relative w-full cursor-text overflow-y-auto caret-primary selection:bg-brand/25 [&_.slate-selection-area]:border [&_.slate-selection-area]:border-brand/25 [&_.slate-selection-area]:bg-brand/15',\n {\n defaultVariants: {\n variant: 'default',\n },\n variants: {\n variant: {\n default: 'h-full',\n demo: 'h-[650px]',\n },\n },\n }\n);\n\nexport const EditorContainer = ({\n className,\n variant,\n ...props\n}: React.HTMLAttributes &\n VariantProps) => {\n const editor = useEditorRef();\n const containerRef = useEditorContainerRef();\n\n return (\n \n );\n};\n\nEditorContainer.displayName = 'EditorContainer';\n\nconst editorVariants = cva(\n cn(\n 'group/editor',\n 'relative w-full overflow-x-hidden whitespace-pre-wrap break-words',\n 'rounded-md ring-offset-background placeholder:text-muted-foreground/80 focus-visible:outline-none',\n '[&_[data-slate-placeholder]]:text-muted-foreground/80 [&_[data-slate-placeholder]]:!opacity-100',\n '[&_[data-slate-placeholder]]:top-[auto_!important]',\n '[&_strong]:font-bold'\n ),\n {\n defaultVariants: {\n variant: 'default',\n },\n variants: {\n disabled: {\n true: 'cursor-not-allowed opacity-50',\n },\n focused: {\n true: 'ring-2 ring-ring ring-offset-2',\n },\n variant: {\n ai: 'w-full px-0 text-base md:text-sm',\n aiChat:\n 'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-3 py-2 text-base md:text-sm',\n default:\n 'size-full px-16 pb-72 pt-4 text-base sm:px-[max(64px,calc(50%-350px))]',\n demo: 'size-full px-16 pb-72 pt-4 text-base sm:px-[max(64px,calc(50%-350px))]',\n fullWidth: 'size-full px-16 pb-72 pt-4 text-base sm:px-24',\n none: '',\n },\n },\n }\n);\n\nexport type EditorProps = PlateContentProps &\n VariantProps;\n\nexport const Editor = React.forwardRef(\n ({ className, disabled, focused, variant, ...props }, ref) => {\n return (\n \n );\n }\n);\n\nEditor.displayName = 'Editor';\n", + "content": "'use client';\n\nimport React from 'react';\n\nimport type { PlateContentProps } from '@udecode/plate-common/react';\nimport type { VariantProps } from 'class-variance-authority';\n\nimport { cn } from '@udecode/cn';\nimport {\n PlateContent,\n useEditorContainerRef,\n useEditorRef,\n} from '@udecode/plate-common/react';\nimport { cva } from 'class-variance-authority';\n\nconst editorContainerVariants = cva(\n 'relative w-full cursor-text overflow-y-auto caret-primary selection:bg-brand/25 [&_.slate-selection-area]:border [&_.slate-selection-area]:border-brand/25 [&_.slate-selection-area]:bg-brand/15',\n {\n defaultVariants: {\n variant: 'default',\n },\n variants: {\n variant: {\n default: 'h-full',\n demo: 'h-[650px]',\n },\n },\n }\n);\n\nexport const EditorContainer = ({\n className,\n variant,\n ...props\n}: React.HTMLAttributes &\n VariantProps) => {\n const editor = useEditorRef();\n const containerRef = useEditorContainerRef();\n\n return (\n \n );\n};\n\nEditorContainer.displayName = 'EditorContainer';\n\nconst editorVariants = cva(\n cn(\n 'group/editor',\n 'relative w-full overflow-x-hidden whitespace-pre-wrap break-words',\n 'rounded-md ring-offset-background placeholder:text-muted-foreground/80 focus-visible:outline-none',\n '[&_[data-slate-placeholder]]:text-muted-foreground/80 [&_[data-slate-placeholder]]:!opacity-100',\n '[&_[data-slate-placeholder]]:top-[auto_!important]',\n '[&_strong]:font-bold'\n ),\n {\n defaultVariants: {\n variant: 'default',\n },\n variants: {\n disabled: {\n true: 'cursor-not-allowed opacity-50',\n },\n focused: {\n true: 'ring-2 ring-ring ring-offset-2',\n },\n variant: {\n ai: 'w-full px-0 text-base md:text-sm',\n aiChat:\n 'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-3 py-2 text-base md:text-sm',\n default:\n 'size-full px-16 pb-72 pt-4 text-base sm:px-[max(64px,calc(50%-350px))]',\n demo: 'size-full px-16 pb-72 pt-4 text-base sm:px-[max(64px,calc(50%-350px))]',\n fullWidth: 'size-full px-16 pb-72 pt-4 text-base sm:px-24',\n none: '',\n },\n },\n }\n);\n\nexport type EditorProps = PlateContentProps &\n VariantProps;\n\nexport const Editor = React.forwardRef(\n ({ className, disabled, focused, variant, ...props }, ref) => {\n return (\n \n );\n }\n);\n\nEditor.displayName = 'Editor';\n", "path": "plate-ui/editor.tsx", "target": "components/plate-ui/editor.tsx", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/export-toolbar-button.json b/apps/www/public/r/styles/default/export-toolbar-button.json index de06793503..d020e5b3ae 100644 --- a/apps/www/public/r/styles/default/export-toolbar-button.json +++ b/apps/www/public/r/styles/default/export-toolbar-button.json @@ -19,7 +19,7 @@ }, "files": [ { - "content": "'use client';\n\nimport React from 'react';\n\nimport { withRef } from '@udecode/cn';\nimport { toDOMNode, useEditorRef } from '@udecode/plate-common/react';\nimport { ArrowDownToLineIcon } from 'lucide-react';\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuTrigger,\n useOpenState,\n} from './dropdown-menu';\nimport {\n ToolbarSplitButton,\n ToolbarSplitButtonPrimary,\n ToolbarSplitButtonSecondary,\n} from './toolbar';\n\nexport const ExportToolbarButton = withRef(\n ({ children, ...props }, ref) => {\n const editor = useEditorRef();\n const openState = useOpenState();\n\n const getCanvas = async () => {\n const { default: html2canvas } = await import('html2canvas');\n\n const style = document.createElement('style');\n document.head.append(style);\n style.sheet?.insertRule(\n 'body > div:last-child img { display: inline-block !important; }'\n );\n\n const canvas = await html2canvas(toDOMNode(editor, editor)!);\n style.remove();\n\n return canvas;\n };\n\n const downloadFile = (href: string, filename: string) => {\n const element = document.createElement('a');\n element.setAttribute('href', href);\n element.setAttribute('download', filename);\n element.style.display = 'none';\n document.body.append(element);\n element.click();\n element.remove();\n };\n\n const exportToPdf = async () => {\n const canvas = await getCanvas();\n\n const PDFLib = await import('pdf-lib');\n const pdfDoc = await PDFLib.PDFDocument.create();\n const page = pdfDoc.addPage([canvas.width, canvas.height]);\n const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));\n\n page.drawImage(imageEmbed, {\n height: canvas.height,\n width: canvas.width,\n x: 0,\n y: 0,\n });\n const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });\n\n downloadFile(pdfBase64, 'plate.pdf');\n };\n\n const exportToImage = async () => {\n const canvas = await getCanvas();\n downloadFile(canvas.toDataURL('image/png'), 'plate.png');\n };\n\n return (\n {\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n openState.onOpenChange(true);\n }\n }}\n pressed={openState.open}\n tooltip=\"Export\"\n {...props}\n >\n \n \n \n\n \n \n \n \n\n e.stopPropagation()}\n align=\"start\"\n alignOffset={-32}\n >\n \n \n Export as PDF\n \n \n Export via Image\n \n \n \n \n \n );\n }\n);\n", + "content": "'use client';\n\nimport React from 'react';\n\nimport type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';\n\nimport { toDOMNode, useEditorRef } from '@udecode/plate-common/react';\nimport { ArrowDownToLineIcon } from 'lucide-react';\n\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuGroup,\n DropdownMenuItem,\n DropdownMenuTrigger,\n useOpenState,\n} from './dropdown-menu';\nimport { ToolbarButton } from './toolbar';\n\nexport function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {\n const editor = useEditorRef();\n const openState = useOpenState();\n\n const getCanvas = async () => {\n const { default: html2canvas } = await import('html2canvas');\n\n const style = document.createElement('style');\n document.head.append(style);\n style.sheet?.insertRule(\n 'body > div:last-child img { display: inline-block !important; }'\n );\n\n const canvas = await html2canvas(toDOMNode(editor, editor)!);\n style.remove();\n\n return canvas;\n };\n\n const downloadFile = (href: string, filename: string) => {\n const element = document.createElement('a');\n element.setAttribute('href', href);\n element.setAttribute('download', filename);\n element.style.display = 'none';\n document.body.append(element);\n element.click();\n element.remove();\n };\n\n const exportToPdf = async () => {\n const canvas = await getCanvas();\n\n const PDFLib = await import('pdf-lib');\n const pdfDoc = await PDFLib.PDFDocument.create();\n const page = pdfDoc.addPage([canvas.width, canvas.height]);\n const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG'));\n const { height, width } = imageEmbed.scale(1);\n page.drawImage(imageEmbed, {\n height,\n width,\n x: 0,\n y: 0,\n });\n const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });\n\n downloadFile(pdfBase64, 'plate.pdf');\n };\n\n const exportToImage = async () => {\n const canvas = await getCanvas();\n downloadFile(canvas.toDataURL('image/png'), 'plate.png');\n };\n\n return (\n \n \n \n \n \n \n\n \n \n \n Export as PDF\n \n \n Export as Image\n \n \n \n \n );\n}\n", "path": "plate-ui/export-toolbar-button.tsx", "target": "components/plate-ui/export-toolbar-button.tsx", "type": "registry:ui" diff --git a/apps/www/public/r/styles/default/fixed-toolbar-buttons.json b/apps/www/public/r/styles/default/fixed-toolbar-buttons.json index 0b21aa2448..c6e3dda1b5 100644 --- a/apps/www/public/r/styles/default/fixed-toolbar-buttons.json +++ b/apps/www/public/r/styles/default/fixed-toolbar-buttons.json @@ -14,7 +14,7 @@ }, "files": [ { - "content": "'use client';\n\nimport React from 'react';\n\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { useEditorReadOnly } from '@udecode/plate-common/react';\nimport {\n FontBackgroundColorPlugin,\n FontColorPlugin,\n} from '@udecode/plate-font/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { ListStyleType } from '@udecode/plate-indent-list';\nimport {\n AudioPlugin,\n FilePlugin,\n ImagePlugin,\n VideoPlugin,\n} from '@udecode/plate-media/react';\nimport {\n ArrowUpToLineIcon,\n BaselineIcon,\n BoldIcon,\n Code2Icon,\n HighlighterIcon,\n ItalicIcon,\n PaintBucketIcon,\n StrikethroughIcon,\n UnderlineIcon,\n WandSparklesIcon,\n} from 'lucide-react';\n\nimport { MoreDropdownMenu } from '@/components/plate-ui/more-dropdown-menu';\n\nimport { AIToolbarButton } from './ai-toolbar-button';\nimport { AlignDropdownMenu } from './align-dropdown-menu';\nimport { ColorDropdownMenu } from './color-dropdown-menu';\nimport { CommentToolbarButton } from './comment-toolbar-button';\nimport { EmojiDropdownMenu } from './emoji-dropdown-menu';\nimport { ExportToolbarButton } from './export-toolbar-button';\nimport { RedoToolbarButton, UndoToolbarButton } from './history-toolbar-button';\nimport { IndentListToolbarButton } from './indent-list-toolbar-button';\nimport { IndentTodoToolbarButton } from './indent-todo-toolbar-button';\nimport { IndentToolbarButton } from './indent-toolbar-button';\nimport { InsertDropdownMenu } from './insert-dropdown-menu';\nimport { LineHeightDropdownMenu } from './line-height-dropdown-menu';\nimport { LinkToolbarButton } from './link-toolbar-button';\nimport { MarkToolbarButton } from './mark-toolbar-button';\nimport { MediaToolbarButton } from './media-toolbar-button';\nimport { ModeDropdownMenu } from './mode-dropdown-menu';\nimport { OutdentToolbarButton } from './outdent-toolbar-button';\nimport { TableDropdownMenu } from './table-dropdown-menu';\nimport { ToggleToolbarButton } from './toggle-toolbar-button';\nimport { ToolbarGroup } from './toolbar';\nimport { TurnIntoDropdownMenu } from './turn-into-dropdown-menu';\n\nexport function FixedToolbarButtons() {\n const readOnly = useEditorReadOnly();\n\n return (\n
\n {!readOnly && (\n <>\n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n\n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n )}\n\n
\n\n \n \n \n \n \n \n\n \n \n \n
\n );\n}\n", + "content": "'use client';\n\nimport React from 'react';\n\nimport {\n BoldPlugin,\n CodePlugin,\n ItalicPlugin,\n StrikethroughPlugin,\n UnderlinePlugin,\n} from '@udecode/plate-basic-marks/react';\nimport { useEditorReadOnly } from '@udecode/plate-common/react';\nimport {\n FontBackgroundColorPlugin,\n FontColorPlugin,\n} from '@udecode/plate-font/react';\nimport { HighlightPlugin } from '@udecode/plate-highlight/react';\nimport { ListStyleType } from '@udecode/plate-indent-list';\nimport {\n AudioPlugin,\n FilePlugin,\n ImagePlugin,\n VideoPlugin,\n} from '@udecode/plate-media/react';\nimport {\n ArrowUpToLineIcon,\n BaselineIcon,\n BoldIcon,\n Code2Icon,\n HighlighterIcon,\n ItalicIcon,\n PaintBucketIcon,\n StrikethroughIcon,\n UnderlineIcon,\n WandSparklesIcon,\n} from 'lucide-react';\n\nimport { MoreDropdownMenu } from '@/components/plate-ui/more-dropdown-menu';\n\nimport { AIToolbarButton } from './ai-toolbar-button';\nimport { AlignDropdownMenu } from './align-dropdown-menu';\nimport { ColorDropdownMenu } from './color-dropdown-menu';\nimport { CommentToolbarButton } from './comment-toolbar-button';\nimport { EmojiDropdownMenu } from './emoji-dropdown-menu';\nimport { ExportToolbarButton } from './export-toolbar-button';\nimport { RedoToolbarButton, UndoToolbarButton } from './history-toolbar-button';\nimport { IndentListToolbarButton } from './indent-list-toolbar-button';\nimport { IndentTodoToolbarButton } from './indent-todo-toolbar-button';\nimport { IndentToolbarButton } from './indent-toolbar-button';\nimport { InsertDropdownMenu } from './insert-dropdown-menu';\nimport { LineHeightDropdownMenu } from './line-height-dropdown-menu';\nimport { LinkToolbarButton } from './link-toolbar-button';\nimport { MarkToolbarButton } from './mark-toolbar-button';\nimport { MediaToolbarButton } from './media-toolbar-button';\nimport { ModeDropdownMenu } from './mode-dropdown-menu';\nimport { OutdentToolbarButton } from './outdent-toolbar-button';\nimport { TableDropdownMenu } from './table-dropdown-menu';\nimport { ToggleToolbarButton } from './toggle-toolbar-button';\nimport { ToolbarGroup } from './toolbar';\nimport { TurnIntoDropdownMenu } from './turn-into-dropdown-menu';\n\nexport function FixedToolbarButtons() {\n const readOnly = useEditorReadOnly();\n\n return (\n
\n {!readOnly && (\n <>\n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n\n \n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n\n \n \n \n \n\n \n \n\n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n \n \n\n \n \n \n \n \n\n \n \n \n \n )}\n\n
\n\n \n \n \n \n \n \n\n \n \n \n
\n );\n}\n", "path": "plate-ui/fixed-toolbar-buttons.tsx", "target": "components/plate-ui/fixed-toolbar-buttons.tsx", "type": "registry:ui" @@ -42,7 +42,8 @@ "outdent-toolbar-button", "table-dropdown-menu", "toggle-toolbar-button", - "turn-into-dropdown-menu" + "turn-into-dropdown-menu", + "export-toolbar-button" ], "type": "registry:ui" } \ No newline at end of file diff --git a/apps/www/src/__registry__/index.tsx b/apps/www/src/__registry__/index.tsx index ae0e0695c2..570153d177 100644 --- a/apps/www/src/__registry__/index.tsx +++ b/apps/www/src/__registry__/index.tsx @@ -1149,7 +1149,7 @@ export const Index: Record = { name: "fixed-toolbar-buttons", description: "", type: "registry:ui", - registryDependencies: ["toolbar","ai-toolbar-button","align-dropdown-menu","color-dropdown-menu","comment-toolbar-button","emoji-dropdown-menu","history-toolbar-button","indent-list-toolbar-button","indent-todo-toolbar-button","indent-toolbar-button","insert-dropdown-menu","line-height-dropdown-menu","link-toolbar-button","mark-toolbar-button","media-toolbar-button","mode-dropdown-menu","more-dropdown-menu","outdent-toolbar-button","table-dropdown-menu","toggle-toolbar-button","turn-into-dropdown-menu"], + registryDependencies: ["toolbar","ai-toolbar-button","align-dropdown-menu","color-dropdown-menu","comment-toolbar-button","emoji-dropdown-menu","history-toolbar-button","indent-list-toolbar-button","indent-todo-toolbar-button","indent-toolbar-button","insert-dropdown-menu","line-height-dropdown-menu","link-toolbar-button","mark-toolbar-button","media-toolbar-button","mode-dropdown-menu","more-dropdown-menu","outdent-toolbar-button","table-dropdown-menu","toggle-toolbar-button","turn-into-dropdown-menu","export-toolbar-button"], files: [{ path: "src/registry/default/plate-ui/fixed-toolbar-buttons.tsx", type: "registry:ui", diff --git a/templates/plate-playground-template/package.json b/templates/plate-playground-template/package.json index 6f7930a8f6..652ec63ab0 100644 --- a/templates/plate-playground-template/package.json +++ b/templates/plate-playground-template/package.json @@ -79,8 +79,10 @@ "clsx": "^2.1.1", "cmdk": "^1.0.4", "date-fns": "^4.1.0", + "html2canvas": "^1.4.1", "lucide-react": "0.456.0", "next": "^15.0.3", + "pdf-lib": "^1.17.1", "prismjs": "^1.29.0", "react": "^18.3.1", "react-day-picker": "8.10.1", diff --git a/templates/plate-playground-template/pnpm-lock.yaml b/templates/plate-playground-template/pnpm-lock.yaml index 94d988fd30..858dd81400 100644 --- a/templates/plate-playground-template/pnpm-lock.yaml +++ b/templates/plate-playground-template/pnpm-lock.yaml @@ -206,12 +206,18 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + html2canvas: + specifier: ^1.4.1 + version: 1.4.1 lucide-react: specifier: 0.456.0 version: 0.456.0(react@18.3.1) next: specifier: ^15.0.3 version: 15.0.3(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + pdf-lib: + specifier: ^1.17.1 + version: 1.17.1 prismjs: specifier: ^1.29.0 version: 1.29.0 @@ -884,6 +890,12 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} + '@pdf-lib/standard-fonts@1.0.0': + resolution: {integrity: sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==} + + '@pdf-lib/upng@1.0.1': + resolution: {integrity: sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2334,6 +2346,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -2462,6 +2478,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + css-select@4.3.0: resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} @@ -3032,6 +3051,10 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + htmlparser2@5.0.1: resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} @@ -3627,6 +3650,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3660,6 +3686,9 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + pdf-lib@1.17.1: + resolution: {integrity: sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==} + performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} @@ -4180,6 +4209,9 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -4225,6 +4257,9 @@ packages: tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -4388,6 +4423,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + valid-data-url@3.0.1: resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} engines: {node: '>=10'} @@ -4914,6 +4952,14 @@ snapshots: '@opentelemetry/api@1.9.0': {} + '@pdf-lib/standard-fonts@1.0.0': + dependencies: + pako: 1.0.11 + + '@pdf-lib/upng@1.0.1': + dependencies: + pako: 1.0.11 + '@pkgjs/parseargs@0.11.0': optional: true @@ -6518,6 +6564,8 @@ snapshots: balanced-match@1.0.2: {} + base64-arraybuffer@1.0.2: {} + binary-extensions@2.3.0: {} boolbase@1.0.0: {} @@ -6663,6 +6711,10 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + css-select@4.3.0: dependencies: boolbase: 1.0.0 @@ -7385,6 +7437,11 @@ snapshots: dependencies: react-is: 16.13.1 + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + htmlparser2@5.0.1: dependencies: domelementtype: 2.3.0 @@ -8124,6 +8181,8 @@ snapshots: package-json-from-dist@1.0.1: {} + pako@1.0.11: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -8149,6 +8208,13 @@ snapshots: path-type@4.0.0: {} + pdf-lib@1.17.1: + dependencies: + '@pdf-lib/standard-fonts': 1.0.0 + '@pdf-lib/upng': 1.0.1 + pako: 1.0.11 + tslib: 1.14.1 + performance-now@2.1.0: {} picocolors@1.1.0: {} @@ -8760,6 +8826,10 @@ snapshots: tapable@2.2.1: {} + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + text-table@0.2.0: {} thenify-all@1.6.0: @@ -8799,6 +8869,8 @@ snapshots: minimist: 1.2.8 strip-bom: 3.0.0 + tslib@1.14.1: {} + tslib@2.8.1: {} tsx@4.19.2: @@ -8961,6 +9033,10 @@ snapshots: util-deprecate@1.0.2: {} + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + valid-data-url@3.0.1: {} validator@13.12.0: {} diff --git a/templates/plate-playground-template/src/components/plate-ui/button.tsx b/templates/plate-playground-template/src/components/plate-ui/button.tsx index d017db41fe..a4be477f5e 100644 --- a/templates/plate-playground-template/src/components/plate-ui/button.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/button.tsx @@ -17,7 +17,7 @@ export const buttonVariants = cva( }, size: { icon: 'size-[28px] rounded-md px-1.5', - lg: 'h-10 rounded-md px-4', + lg: 'h-9 rounded-md px-4', md: 'h-8 px-3 text-sm', none: '', sm: 'h-[28px] rounded-md px-2.5', diff --git a/templates/plate-playground-template/src/components/plate-ui/editor.tsx b/templates/plate-playground-template/src/components/plate-ui/editor.tsx index 6810bb171c..093b79d107 100644 --- a/templates/plate-playground-template/src/components/plate-ui/editor.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/editor.tsx @@ -46,7 +46,6 @@ export const EditorContainer = ({ editorContainerVariants({ variant }), className )} - role="button" {...props} /> ); @@ -75,9 +74,9 @@ const editorVariants = cva( true: 'ring-2 ring-ring ring-offset-2', }, variant: { - ai: 'w-full px-0 text-sm', + ai: 'w-full px-0 text-base md:text-sm', aiChat: - 'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-3 py-2 text-sm', + 'max-h-[min(70vh,320px)] w-full max-w-[700px] overflow-y-auto px-3 py-2 text-base md:text-sm', default: 'size-full px-16 pb-72 pt-4 text-base sm:px-[max(64px,calc(50%-350px))]', demo: 'size-full px-16 pb-72 pt-4 text-base sm:px-[max(64px,calc(50%-350px))]', diff --git a/templates/plate-playground-template/src/components/plate-ui/export-toolbar-button.tsx b/templates/plate-playground-template/src/components/plate-ui/export-toolbar-button.tsx new file mode 100644 index 0000000000..a659a8bf70 --- /dev/null +++ b/templates/plate-playground-template/src/components/plate-ui/export-toolbar-button.tsx @@ -0,0 +1,93 @@ +'use client'; + +import React from 'react'; + +import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu'; + +import { toDOMNode, useEditorRef } from '@udecode/plate-common/react'; +import { ArrowDownToLineIcon } from 'lucide-react'; + +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuTrigger, + useOpenState, +} from './dropdown-menu'; +import { ToolbarButton } from './toolbar'; + +export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) { + const editor = useEditorRef(); + const openState = useOpenState(); + + const getCanvas = async () => { + const { default: html2canvas } = await import('html2canvas'); + + const style = document.createElement('style'); + document.head.append(style); + style.sheet?.insertRule( + 'body > div:last-child img { display: inline-block !important; }' + ); + + const canvas = await html2canvas(toDOMNode(editor, editor)!); + style.remove(); + + return canvas; + }; + + const downloadFile = (href: string, filename: string) => { + const element = document.createElement('a'); + element.setAttribute('href', href); + element.setAttribute('download', filename); + element.style.display = 'none'; + document.body.append(element); + element.click(); + element.remove(); + }; + + const exportToPdf = async () => { + const canvas = await getCanvas(); + + const PDFLib = await import('pdf-lib'); + const pdfDoc = await PDFLib.PDFDocument.create(); + const page = pdfDoc.addPage([canvas.width, canvas.height]); + const imageEmbed = await pdfDoc.embedPng(canvas.toDataURL('PNG')); + const { height, width } = imageEmbed.scale(1); + page.drawImage(imageEmbed, { + height, + width, + x: 0, + y: 0, + }); + const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true }); + + downloadFile(pdfBase64, 'plate.pdf'); + }; + + const exportToImage = async () => { + const canvas = await getCanvas(); + downloadFile(canvas.toDataURL('image/png'), 'plate.png'); + }; + + return ( + + + + + + + + + + + Export as PDF + + + Export as Image + + + + + ); +} diff --git a/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx b/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx index 4b26dcf78c..9d93e84829 100644 --- a/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/fixed-toolbar-buttons.tsx @@ -23,6 +23,7 @@ import { VideoPlugin, } from '@udecode/plate-media/react'; import { + ArrowUpToLineIcon, BaselineIcon, BoldIcon, Code2Icon, @@ -41,6 +42,7 @@ import { AlignDropdownMenu } from './align-dropdown-menu'; import { ColorDropdownMenu } from './color-dropdown-menu'; import { CommentToolbarButton } from './comment-toolbar-button'; import { EmojiDropdownMenu } from './emoji-dropdown-menu'; +import { ExportToolbarButton } from './export-toolbar-button'; import { RedoToolbarButton, UndoToolbarButton } from './history-toolbar-button'; import { IndentListToolbarButton } from './indent-list-toolbar-button'; import { IndentTodoToolbarButton } from './indent-todo-toolbar-button'; @@ -75,6 +77,12 @@ export function FixedToolbarButtons() { + + + + + + diff --git a/templates/plate-playground-template/src/components/plate-ui/input.tsx b/templates/plate-playground-template/src/components/plate-ui/input.tsx index ee0a423dca..ce7a709586 100644 --- a/templates/plate-playground-template/src/components/plate-ui/input.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/input.tsx @@ -4,7 +4,7 @@ import { cn, withVariants } from '@udecode/cn'; import { type VariantProps, cva } from 'class-variance-authority'; export const inputVariants = cva( - 'flex w-full rounded-md bg-transparent text-sm file:border-0 file:bg-background file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50', + 'flex w-full rounded-md bg-transparent text-base file:border-0 file:bg-background file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm', { defaultVariants: { h: 'md', diff --git a/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx b/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx index fa0b95a48c..c3000126da 100644 --- a/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/media-audio-element.tsx @@ -24,7 +24,6 @@ export const MediaAudioElement = withHOC( >
- {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
diff --git a/templates/plate-playground-template/src/components/plate-ui/toc-element.tsx b/templates/plate-playground-template/src/components/plate-ui/toc-element.tsx index 844b2bed28..a282c8d5b3 100644 --- a/templates/plate-playground-template/src/components/plate-ui/toc-element.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/toc-element.tsx @@ -37,7 +37,7 @@ export const TocElement = withRef( className={cn('relative mb-1 p-0', className)} {...props} > - +
{children} ); diff --git a/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx b/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx index 9200d1def2..1d27f55edd 100644 --- a/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx +++ b/templates/plate-playground-template/src/components/plate-ui/toolbar.tsx @@ -81,7 +81,6 @@ const dropdownArrowVariants = cva( ); const ToolbarButton = withTooltip( - // eslint-disable-next-line react/display-name React.forwardRef< React.ElementRef, {