✨(app-impress) button editor convert from markdown
Some people prefer to write in markdown, or had already written stuff in markdown, so this commit adds a button to the editor t hat will convert the markdown to the editor's format.
This commit is contained in:
@@ -77,4 +77,34 @@ test.describe('Pad Editor', () => {
|
||||
expect(pdfText).toContain('La directrice'); // This is the template text
|
||||
expect(pdfText).toContain('Hello World'); // This is the pad text
|
||||
});
|
||||
|
||||
test('markdown button converts from markdown to the editor syntax json', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
const randomPad = await createPad(page, 'pad-markdown', browserName, 1);
|
||||
|
||||
await expect(page.locator('h2').getByText(randomPad[0])).toBeVisible();
|
||||
|
||||
await page.locator('.ProseMirror.bn-editor').click();
|
||||
await page
|
||||
.locator('.ProseMirror.bn-editor')
|
||||
.fill('[test markdown](http://test-markdown.html)');
|
||||
|
||||
await expect(page.getByText('[test markdown]')).toBeVisible();
|
||||
|
||||
await page.getByText('[test markdown]').dblclick();
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'M',
|
||||
})
|
||||
.click();
|
||||
|
||||
await expect(page.getByText('[test markdown]')).toBeHidden();
|
||||
await expect(
|
||||
page.getByRole('link', {
|
||||
name: 'test markdown',
|
||||
}),
|
||||
).toHaveAttribute('href', 'http://test-markdown.html');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ import { usePadStore } from '../stores';
|
||||
import { Pad } from '../types';
|
||||
import { randomColor } from '../utils';
|
||||
|
||||
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
||||
|
||||
interface BlockNoteEditorProps {
|
||||
pad: Pad;
|
||||
}
|
||||
@@ -67,7 +69,9 @@ export const BlockNoteContent = ({ pad, provider }: BlockNoteContentProps) => {
|
||||
};
|
||||
`}
|
||||
>
|
||||
<BlockNoteView editor={editor} />
|
||||
<BlockNoteView editor={editor} formattingToolbar={false}>
|
||||
<BlockNoteToolbar />
|
||||
</BlockNoteView>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
import {
|
||||
BasicTextStyleButton,
|
||||
BlockTypeSelect,
|
||||
ColorStyleButton,
|
||||
CreateLinkButton,
|
||||
FormattingToolbar,
|
||||
FormattingToolbarController,
|
||||
ImageCaptionButton,
|
||||
NestBlockButton,
|
||||
ReplaceImageButton,
|
||||
TextAlignButton,
|
||||
ToolbarButton,
|
||||
UnnestBlockButton,
|
||||
useBlockNoteEditor,
|
||||
} from '@blocknote/react';
|
||||
import '@blocknote/react/style.css';
|
||||
import { forEach, isArray } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
export const BlockNoteToolbar = () => {
|
||||
return (
|
||||
<FormattingToolbarController
|
||||
formattingToolbar={() => (
|
||||
<FormattingToolbar>
|
||||
<BlockTypeSelect key="blockTypeSelect" />
|
||||
|
||||
{/* Extra button to convert from markdown to json */}
|
||||
<MarkdownButton key="customButton" />
|
||||
|
||||
<ImageCaptionButton key="imageCaptionButton" />
|
||||
<ReplaceImageButton key="replaceImageButton" />
|
||||
|
||||
<BasicTextStyleButton basicTextStyle="bold" key="boldStyleButton" />
|
||||
<BasicTextStyleButton
|
||||
basicTextStyle="italic"
|
||||
key="italicStyleButton"
|
||||
/>
|
||||
<BasicTextStyleButton
|
||||
basicTextStyle="underline"
|
||||
key="underlineStyleButton"
|
||||
/>
|
||||
<BasicTextStyleButton
|
||||
basicTextStyle="strike"
|
||||
key="strikeStyleButton"
|
||||
/>
|
||||
{/* Extra button to toggle code styles */}
|
||||
<BasicTextStyleButton key="codeStyleButton" basicTextStyle="code" />
|
||||
|
||||
<TextAlignButton textAlignment="left" key="textAlignLeftButton" />
|
||||
<TextAlignButton textAlignment="center" key="textAlignCenterButton" />
|
||||
<TextAlignButton textAlignment="right" key="textAlignRightButton" />
|
||||
|
||||
<ColorStyleButton key="colorStyleButton" />
|
||||
|
||||
<NestBlockButton key="nestBlockButton" />
|
||||
<UnnestBlockButton key="unnestBlockButton" />
|
||||
|
||||
<CreateLinkButton key="createLinkButton" />
|
||||
</FormattingToolbar>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type Block = {
|
||||
type: string;
|
||||
text: string;
|
||||
content: Block[];
|
||||
};
|
||||
|
||||
function isBlock(block: Block): block is Block {
|
||||
return (
|
||||
block.content &&
|
||||
isArray(block.content) &&
|
||||
block.content.length > 0 &&
|
||||
typeof block.type !== 'undefined'
|
||||
);
|
||||
}
|
||||
|
||||
const recursiveContent = (content: Block[], base: string = '') => {
|
||||
let fullContent = base;
|
||||
for (const innerContent of content) {
|
||||
if (innerContent.type === 'text') {
|
||||
fullContent += innerContent.text;
|
||||
} else if (isBlock(innerContent)) {
|
||||
fullContent = recursiveContent(innerContent.content, fullContent);
|
||||
}
|
||||
}
|
||||
|
||||
return fullContent;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom Formatting Toolbar Button to convert markdown to json.
|
||||
*/
|
||||
export function MarkdownButton() {
|
||||
const editor = useBlockNoteEditor();
|
||||
|
||||
const handleConvertMarkdown = () => {
|
||||
const blocks = editor.getSelection()?.blocks;
|
||||
|
||||
forEach(blocks, async (block) => {
|
||||
if (!isBlock(block as unknown as Block)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const fullContent = recursiveContent(
|
||||
block.content as unknown as Block[],
|
||||
);
|
||||
|
||||
const blockMarkdown =
|
||||
await editor.tryParseMarkdownToBlocks(fullContent);
|
||||
editor.replaceBlocks([block.id], blockMarkdown);
|
||||
} catch (error) {
|
||||
console.error('Error parsing Markdown:', error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<ToolbarButton
|
||||
mainTooltip="Convert Markdown"
|
||||
onClick={handleConvertMarkdown}
|
||||
>
|
||||
M
|
||||
</ToolbarButton>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user