Skip to content

Commit

Permalink
Merge branch 'main' into feat/dnd-above-nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
zbeyens committed Dec 20, 2024
2 parents e20c8a2 + 2e25ff7 commit 4fa87c4
Show file tree
Hide file tree
Showing 2 changed files with 290 additions and 4 deletions.
2 changes: 1 addition & 1 deletion apps/www/content/docs/components/changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Since Plate UI is not a component library, a changelog is maintained here.

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


## December 2024 #17

### December 20 #17.3
Expand All @@ -30,6 +29,7 @@ const onColumnChange = (widths: string[]) => {
});
};
```
- `export-toolbar-button`: add `exportToHtml`

### December 19 #17.2

Expand Down
292 changes: 289 additions & 3 deletions apps/www/src/registry/default/plate-ui/export-toolbar-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,105 @@ import React from 'react';

import type { DropdownMenuProps } from '@radix-ui/react-dropdown-menu';

import { withProps } from '@udecode/cn';
import { BaseAlignPlugin } from '@udecode/plate-alignment';
import {
BaseBoldPlugin,
BaseCodePlugin,
BaseItalicPlugin,
BaseStrikethroughPlugin,
BaseSubscriptPlugin,
BaseSuperscriptPlugin,
BaseUnderlinePlugin,
} from '@udecode/plate-basic-marks';
import { BaseBlockquotePlugin } from '@udecode/plate-block-quote';
import {
BaseCodeBlockPlugin,
BaseCodeLinePlugin,
BaseCodeSyntaxPlugin,
} from '@udecode/plate-code-block';
import { BaseCommentsPlugin } from '@udecode/plate-comments';
import {
BaseParagraphPlugin,
SlateLeaf,
createSlateEditor,
serializeHtml,
} from '@udecode/plate-common';
import { toDOMNode, useEditorRef } from '@udecode/plate-common/react';
import { BaseDatePlugin } from '@udecode/plate-date';
import {
BaseFontBackgroundColorPlugin,
BaseFontColorPlugin,
BaseFontSizePlugin,
} from '@udecode/plate-font';
import {
BaseHeadingPlugin,
BaseTocPlugin,
HEADING_KEYS,
HEADING_LEVELS,
} from '@udecode/plate-heading';
import { BaseHighlightPlugin } from '@udecode/plate-highlight';
import { BaseHorizontalRulePlugin } from '@udecode/plate-horizontal-rule';
import { BaseIndentPlugin } from '@udecode/plate-indent';
import { BaseIndentListPlugin } from '@udecode/plate-indent-list';
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 {
BaseAudioPlugin,
BaseFilePlugin,
BaseImagePlugin,
BaseMediaEmbedPlugin,
BaseVideoPlugin,
} from '@udecode/plate-media';
import { BaseMentionPlugin } from '@udecode/plate-mention';
import {
BaseTableCellHeaderPlugin,
BaseTableCellPlugin,
BaseTablePlugin,
BaseTableRowPlugin,
} from '@udecode/plate-table';
import { BaseTogglePlugin } from '@udecode/plate-toggle';
import { ArrowDownToLineIcon } from 'lucide-react';
import Prism from 'prismjs';

import { BlockquoteElementStatic } from '@/registry/default/plate-ui/blockquote-element-static';
import { CodeBlockElementStatic } from '@/registry/default/plate-ui/code-block-element-static';
import { CodeLeafStatic } from '@/registry/default/plate-ui/code-leaf-static';
import { CodeLineElementStatic } from '@/registry/default/plate-ui/code-line-element-static';
import { CodeSyntaxLeafStatic } from '@/registry/default/plate-ui/code-syntax-leaf-static';
import { ColumnElementStatic } from '@/registry/default/plate-ui/column-element-static';
import { ColumnGroupElementStatic } from '@/registry/default/plate-ui/column-group-element-static';
import { CommentLeafStatic } from '@/registry/default/plate-ui/comment-leaf-static';
import { DateElementStatic } from '@/registry/default/plate-ui/date-element-static';
import { HeadingElementStatic } from '@/registry/default/plate-ui/heading-element-static';
import { HighlightLeafStatic } from '@/registry/default/plate-ui/highlight-leaf-static';
import { HrElementStatic } from '@/registry/default/plate-ui/hr-element-static';
import { ImageElementStatic } from '@/registry/default/plate-ui/image-element-static';
import {
FireLiComponent,
FireMarker,
} from '@/registry/default/plate-ui/indent-fire-marker';
import {
TodoLiStatic,
TodoMarkerStatic,
} from '@/registry/default/plate-ui/indent-todo-marker-static';
import { KbdLeafStatic } from '@/registry/default/plate-ui/kbd-leaf-static';
import { LinkElementStatic } from '@/registry/default/plate-ui/link-element-static';
import { MediaAudioElementStatic } from '@/registry/default/plate-ui/media-audio-element-static';
import { MediaFileElementStatic } from '@/registry/default/plate-ui/media-file-element-static';
import { MediaVideoElementStatic } from '@/registry/default/plate-ui/media-video-element-static';
import { MentionElementStatic } from '@/registry/default/plate-ui/mention-element-static';
import { ParagraphElementStatic } from '@/registry/default/plate-ui/paragraph-element-static';
import {
TableCellElementStatic,
TableCellHeaderStaticElement,
} from '@/registry/default/plate-ui/table-cell-element-static';
import { TableElementStatic } from '@/registry/default/plate-ui/table-element-static';
import { TableRowElementStatic } from '@/registry/default/plate-ui/table-row-element-static';
import { TocElementStatic } from '@/registry/default/plate-ui/toc-element-static';
import { ToggleElementStatic } from '@/registry/default/plate-ui/toggle-element-static';

import {
DropdownMenu,
Expand All @@ -15,6 +112,7 @@ import {
DropdownMenuTrigger,
useOpenState,
} from './dropdown-menu';
import { EditorStatic } from './editor-static';
import { ToolbarButton } from './toolbar';

export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
Expand All @@ -36,8 +134,19 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
return canvas;
};

const downloadFile = (href: string, filename: string) => {
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';
Expand All @@ -62,12 +171,186 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {
});
const pdfBase64 = await pdfDoc.saveAsBase64({ dataUri: true });

downloadFile(pdfBase64, 'plate.pdf');
downloadFile({ content: pdfBase64, filename: 'plate.pdf' });
};

const exportToImage = async () => {
const canvas = await getCanvas();
downloadFile(canvas.toDataURL('image/png'), 'plate.png');
downloadFile({
content: canvas.toDataURL('image/png'),
filename: 'plate.png',
});
};

const exportToHtml = async () => {
const components = {
[BaseAudioPlugin.key]: MediaAudioElementStatic,
[BaseBlockquotePlugin.key]: BlockquoteElementStatic,
[BaseBoldPlugin.key]: withProps(SlateLeaf, { as: 'strong' }),
[BaseCodeBlockPlugin.key]: CodeBlockElementStatic,
[BaseCodeLinePlugin.key]: CodeLineElementStatic,
[BaseCodePlugin.key]: CodeLeafStatic,
[BaseCodeSyntaxPlugin.key]: CodeSyntaxLeafStatic,
[BaseColumnItemPlugin.key]: ColumnElementStatic,
[BaseColumnPlugin.key]: ColumnGroupElementStatic,
[BaseCommentsPlugin.key]: CommentLeafStatic,
[BaseDatePlugin.key]: DateElementStatic,
[BaseFilePlugin.key]: MediaFileElementStatic,
[BaseHighlightPlugin.key]: HighlightLeafStatic,
[BaseHorizontalRulePlugin.key]: HrElementStatic,
[BaseImagePlugin.key]: ImageElementStatic,
[BaseItalicPlugin.key]: withProps(SlateLeaf, { as: 'em' }),
[BaseKbdPlugin.key]: KbdLeafStatic,
[BaseLinkPlugin.key]: LinkElementStatic,
// [BaseMediaEmbedPlugin.key]: MediaEmbedElementStatic,
[BaseMentionPlugin.key]: MentionElementStatic,
[BaseParagraphPlugin.key]: ParagraphElementStatic,
[BaseStrikethroughPlugin.key]: withProps(SlateLeaf, { as: 'del' }),
[BaseSubscriptPlugin.key]: withProps(SlateLeaf, { as: 'sub' }),
[BaseSuperscriptPlugin.key]: withProps(SlateLeaf, { as: 'sup' }),
[BaseTableCellHeaderPlugin.key]: TableCellHeaderStaticElement,
[BaseTableCellPlugin.key]: TableCellElementStatic,
[BaseTablePlugin.key]: TableElementStatic,
[BaseTableRowPlugin.key]: TableRowElementStatic,
[BaseTocPlugin.key]: TocElementStatic,
[BaseTogglePlugin.key]: ToggleElementStatic,
[BaseUnderlinePlugin.key]: withProps(SlateLeaf, { as: 'u' }),
[BaseVideoPlugin.key]: MediaVideoElementStatic,
[HEADING_KEYS.h1]: withProps(HeadingElementStatic, { variant: 'h1' }),
[HEADING_KEYS.h2]: withProps(HeadingElementStatic, { variant: 'h2' }),
[HEADING_KEYS.h3]: withProps(HeadingElementStatic, { variant: 'h3' }),
[HEADING_KEYS.h4]: withProps(HeadingElementStatic, { variant: 'h4' }),
[HEADING_KEYS.h5]: withProps(HeadingElementStatic, { variant: 'h5' }),
[HEADING_KEYS.h6]: withProps(HeadingElementStatic, { variant: 'h6' }),
};

const editorStatic = createSlateEditor({
plugins: [
BaseColumnPlugin,
BaseColumnItemPlugin,
BaseTocPlugin,
BaseVideoPlugin,
BaseAudioPlugin,
BaseParagraphPlugin,
BaseHeadingPlugin,
BaseMediaEmbedPlugin,
BaseBoldPlugin,
BaseCodePlugin,
BaseItalicPlugin,
BaseStrikethroughPlugin,
BaseSubscriptPlugin,
BaseSuperscriptPlugin,
BaseUnderlinePlugin,
BaseBlockquotePlugin,
BaseDatePlugin,
BaseCodeBlockPlugin.configure({
options: {
prism: Prism,
},
}),
BaseIndentPlugin.extend({
inject: {
targetPlugins: [
BaseParagraphPlugin.key,
BaseBlockquotePlugin.key,
BaseCodeBlockPlugin.key,
],
},
}),
BaseIndentListPlugin.extend({
inject: {
targetPlugins: [
BaseParagraphPlugin.key,
...HEADING_LEVELS,
BaseBlockquotePlugin.key,
BaseCodeBlockPlugin.key,
BaseTogglePlugin.key,
],
},
options: {
listStyleTypes: {
fire: {
liComponent: FireLiComponent,
markerComponent: FireMarker,
type: 'fire',
},
todo: {
liComponent: TodoLiStatic,
markerComponent: TodoMarkerStatic,
type: 'todo',
},
},
},
}),
BaseLinkPlugin,
BaseTableRowPlugin,
BaseTablePlugin,
BaseTableCellPlugin,
BaseHorizontalRulePlugin,
BaseFontColorPlugin,
BaseFontBackgroundColorPlugin,
BaseFontSizePlugin,
BaseKbdPlugin,
BaseAlignPlugin.extend({
inject: {
targetPlugins: [
BaseParagraphPlugin.key,
BaseMediaEmbedPlugin.key,
...HEADING_LEVELS,
BaseImagePlugin.key,
],
},
}),
BaseLineHeightPlugin,
BaseHighlightPlugin,
BaseFilePlugin,
BaseImagePlugin,
BaseMentionPlugin,
BaseCommentsPlugin,
BaseTogglePlugin,
],
value: editor.children,
});

const editorHtml = await serializeHtml(editorStatic, {
components,
editorComponent: EditorStatic,
props: { style: { padding: '0 calc(50% - 350px)', paddingBottom: '' } },
});

const prismCss = `<link rel="stylesheet" href="https://platejs.org/_next/static/css/274e256ca08ece78.css">`;
const tailwindCss = `
<link rel="stylesheet" href="https://platejs.org/_next/static/css/4d3ab7d3f144d56d.css">
<link rel="stylesheet" href="https://platejs.org/_next/static/css/e8dd5394bd410c4c.css">
`;

const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;700&display=swap"
rel="stylesheet"
/>
${tailwindCss}
${prismCss}
<style>
:root {
--font-sans: 'Inter', sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
</style>
</head>
<body>
${editorHtml}
</body>
</html>`;

downloadFile({ content: html, filename: 'plate.html', isHtml: true });
};

return (
Expand All @@ -80,6 +363,9 @@ export function ExportToolbarButton({ children, ...props }: DropdownMenuProps) {

<DropdownMenuContent align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={exportToHtml}>
Export as HTML
</DropdownMenuItem>
<DropdownMenuItem onSelect={exportToPdf}>
Export as PDF
</DropdownMenuItem>
Expand Down

0 comments on commit 4fa87c4

Please sign in to comment.