✨(frontend) add quote blocks to the editor
Add a custom block to quote in the editor.
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
BlockNoteSchema,
|
BlockNoteSchema,
|
||||||
Dictionary,
|
Dictionary,
|
||||||
|
defaultBlockSpecs,
|
||||||
locales,
|
locales,
|
||||||
withPageBreak,
|
withPageBreak,
|
||||||
} from '@blocknote/core';
|
} from '@blocknote/core';
|
||||||
@@ -26,8 +27,16 @@ import { randomColor } from '../utils';
|
|||||||
|
|
||||||
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
||||||
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
import { BlockNoteToolbar } from './BlockNoteToolbar';
|
||||||
|
import { QuoteBlock } from './custom-blocks';
|
||||||
|
|
||||||
export const blockNoteSchema = withPageBreak(BlockNoteSchema.create());
|
export const blockNoteSchema = withPageBreak(
|
||||||
|
BlockNoteSchema.create({
|
||||||
|
blockSpecs: {
|
||||||
|
...defaultBlockSpecs,
|
||||||
|
quote: QuoteBlock,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
interface BlockNoteEditorProps {
|
interface BlockNoteEditorProps {
|
||||||
doc: Doc;
|
doc: Doc;
|
||||||
@@ -141,8 +150,8 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
editable={!readOnly}
|
editable={!readOnly}
|
||||||
theme="light"
|
theme="light"
|
||||||
>
|
>
|
||||||
<BlockNoteToolbar />
|
|
||||||
<BlockNoteSuggestionMenu />
|
<BlockNoteSuggestionMenu />
|
||||||
|
<BlockNoteToolbar />
|
||||||
</BlockNoteView>
|
</BlockNoteView>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,13 +5,19 @@ import {
|
|||||||
getDefaultReactSlashMenuItems,
|
getDefaultReactSlashMenuItems,
|
||||||
getPageBreakReactSlashMenuItems,
|
getPageBreakReactSlashMenuItems,
|
||||||
useBlockNoteEditor,
|
useBlockNoteEditor,
|
||||||
|
useDictionary,
|
||||||
} from '@blocknote/react';
|
} from '@blocknote/react';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { DocsBlockNoteEditor } from '../types';
|
import { DocsBlockSchema } from '../types';
|
||||||
|
|
||||||
|
import { getQuoteReactSlashMenuItems } from './custom-blocks';
|
||||||
|
|
||||||
export const BlockNoteSuggestionMenu = () => {
|
export const BlockNoteSuggestionMenu = () => {
|
||||||
const editor = useBlockNoteEditor() as DocsBlockNoteEditor;
|
const editor = useBlockNoteEditor<DocsBlockSchema>();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const basicBlocksName = useDictionary().slash_menu.page_break.group;
|
||||||
|
|
||||||
const getSlashMenuItems = useMemo(() => {
|
const getSlashMenuItems = useMemo(() => {
|
||||||
return async (query: string) =>
|
return async (query: string) =>
|
||||||
@@ -20,11 +26,12 @@ export const BlockNoteSuggestionMenu = () => {
|
|||||||
combineByGroup(
|
combineByGroup(
|
||||||
getDefaultReactSlashMenuItems(editor),
|
getDefaultReactSlashMenuItems(editor),
|
||||||
getPageBreakReactSlashMenuItems(editor),
|
getPageBreakReactSlashMenuItems(editor),
|
||||||
|
getQuoteReactSlashMenuItems(editor, t, basicBlocksName),
|
||||||
),
|
),
|
||||||
query,
|
query,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, [editor]);
|
}, [basicBlocksName, editor, t]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SuggestionMenuController
|
<SuggestionMenuController
|
||||||
|
|||||||
@@ -2,19 +2,28 @@ import '@blocknote/mantine/style.css';
|
|||||||
import {
|
import {
|
||||||
FormattingToolbar,
|
FormattingToolbar,
|
||||||
FormattingToolbarController,
|
FormattingToolbarController,
|
||||||
FormattingToolbarProps,
|
blockTypeSelectItems,
|
||||||
getFormattingToolbarItems,
|
getFormattingToolbarItems,
|
||||||
|
useDictionary,
|
||||||
} from '@blocknote/react';
|
} from '@blocknote/react';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { AIGroupButton } from './AIButton';
|
import { AIGroupButton } from './AIButton';
|
||||||
import { MarkdownButton } from './MarkdownButton';
|
import { MarkdownButton } from './MarkdownButton';
|
||||||
|
import { getQuoteFormattingToolbarItems } from './custom-blocks';
|
||||||
|
|
||||||
export const BlockNoteToolbar = () => {
|
export const BlockNoteToolbar = () => {
|
||||||
|
const dict = useDictionary();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const formattingToolbar = useCallback(
|
const formattingToolbar = useCallback(
|
||||||
({ blockTypeSelectItems }: FormattingToolbarProps) => (
|
() => (
|
||||||
<FormattingToolbar>
|
<FormattingToolbar>
|
||||||
{getFormattingToolbarItems(blockTypeSelectItems)}
|
{getFormattingToolbarItems([
|
||||||
|
...blockTypeSelectItems(dict),
|
||||||
|
getQuoteFormattingToolbarItems(t),
|
||||||
|
])}
|
||||||
|
|
||||||
{/* Extra button to do some AI powered actions */}
|
{/* Extra button to do some AI powered actions */}
|
||||||
<AIGroupButton key="AIButton" />
|
<AIGroupButton key="AIButton" />
|
||||||
@@ -23,7 +32,7 @@ export const BlockNoteToolbar = () => {
|
|||||||
<MarkdownButton key="customButton" />
|
<MarkdownButton key="customButton" />
|
||||||
</FormattingToolbar>
|
</FormattingToolbar>
|
||||||
),
|
),
|
||||||
[],
|
[dict, t],
|
||||||
);
|
);
|
||||||
|
|
||||||
return <FormattingToolbarController formattingToolbar={formattingToolbar} />;
|
return <FormattingToolbarController formattingToolbar={formattingToolbar} />;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import { defaultProps, insertOrUpdateBlock } from '@blocknote/core';
|
||||||
|
import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react';
|
||||||
|
import { TFunction } from 'i18next';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { Text } from '@/components';
|
||||||
|
import { useCunninghamTheme } from '@/cunningham';
|
||||||
|
|
||||||
|
import { DocsBlockNoteEditor } from '../../types';
|
||||||
|
|
||||||
|
export const QuoteBlock = createReactBlockSpec(
|
||||||
|
{
|
||||||
|
type: 'quote',
|
||||||
|
propSchema: {
|
||||||
|
textAlignment: defaultProps.textAlignment,
|
||||||
|
textColor: defaultProps.textColor,
|
||||||
|
},
|
||||||
|
content: 'inline',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
render: (props) => {
|
||||||
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
|
const { colorsTokens } = useCunninghamTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Text
|
||||||
|
className="inline-content"
|
||||||
|
$margin="0 0 1rem 0"
|
||||||
|
$padding="0.5rem 1rem"
|
||||||
|
$variation="600"
|
||||||
|
style={{
|
||||||
|
borderLeft: `4px solid ${colorsTokens()['greyscale-300']}`,
|
||||||
|
fontStyle: 'italic',
|
||||||
|
flexGrow: 1,
|
||||||
|
}}
|
||||||
|
ref={props.contentRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getQuoteReactSlashMenuItems = (
|
||||||
|
editor: DocsBlockNoteEditor,
|
||||||
|
t: TFunction<'translation', undefined>,
|
||||||
|
group: string,
|
||||||
|
) => [
|
||||||
|
{
|
||||||
|
title: t('Quote'),
|
||||||
|
onItemClick: () => {
|
||||||
|
insertOrUpdateBlock(editor, {
|
||||||
|
type: 'quote',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
aliases: ['quote', 'blockquote', 'citation'],
|
||||||
|
group,
|
||||||
|
icon: (
|
||||||
|
<Text $isMaterialIcon $size="18px">
|
||||||
|
format_quote
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
subtext: t('Add a quote block'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const getQuoteFormattingToolbarItems = (
|
||||||
|
t: TFunction<'translation', undefined>,
|
||||||
|
): BlockTypeSelectItem => ({
|
||||||
|
name: t('Quote'),
|
||||||
|
type: 'quote',
|
||||||
|
icon: () => (
|
||||||
|
<Text $isMaterialIcon $size="16px">
|
||||||
|
format_quote
|
||||||
|
</Text>
|
||||||
|
),
|
||||||
|
isSelected: (block) => block.type === 'quote',
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './QuoteBlock';
|
||||||
@@ -6,6 +6,10 @@ export const cssEditor = (readonly: boolean) => css`
|
|||||||
& .ProseMirror {
|
& .ProseMirror {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
|
.bn-side-menu[data-block-type='quote'] {
|
||||||
|
height: 46px;
|
||||||
|
}
|
||||||
|
|
||||||
.collaboration-cursor-custom__base {
|
.collaboration-cursor-custom__base {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user