✨(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('La directrice'); // This is the template text
|
||||||
expect(pdfText).toContain('Hello World'); // This is the pad 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 { Pad } from '../types';
|
||||||
import { randomColor } from '../utils';
|
import { randomColor } from '../utils';
|
||||||
|
|
||||||
|
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
||||||
|
|
||||||
interface BlockNoteEditorProps {
|
interface BlockNoteEditorProps {
|
||||||
pad: Pad;
|
pad: Pad;
|
||||||
}
|
}
|
||||||
@@ -67,7 +69,9 @@ export const BlockNoteContent = ({ pad, provider }: BlockNoteContentProps) => {
|
|||||||
};
|
};
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<BlockNoteView editor={editor} />
|
<BlockNoteView editor={editor} formattingToolbar={false}>
|
||||||
|
<BlockNoteToolbar />
|
||||||
|
</BlockNoteView>
|
||||||
</Box>
|
</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