✨(frontend) add multi columns support for editor
We add multi columns support for editor,
now you can add columns to your document.
Works with export.
📄AGPL feature.
This commit is contained in:
@@ -13,6 +13,7 @@ and this project adheres to
|
|||||||
- ✨(backend) allow masking documents from the list view #1171
|
- ✨(backend) allow masking documents from the list view #1171
|
||||||
- ✨(frontend) subdocs can manage link reach #1190
|
- ✨(frontend) subdocs can manage link reach #1190
|
||||||
- ✨(frontend) add duplicate action to doc tree #1175
|
- ✨(frontend) add duplicate action to doc tree #1175
|
||||||
|
- ✨(frontend) add multi columns support for editor #1219
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|||||||
@@ -136,9 +136,10 @@ NODE_ENV=production NEXT_PUBLIC_PUBLISH_AS_MIT=false yarn build
|
|||||||
|
|
||||||
Packages with licences incompatible with the MIT licence:
|
Packages with licences incompatible with the MIT licence:
|
||||||
* `xl-docx-exporter`: [AGPL-3.0](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-docx-exporter/LICENSE),
|
* `xl-docx-exporter`: [AGPL-3.0](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-docx-exporter/LICENSE),
|
||||||
* `xl-pdf-exporter`: [AGPL-3.0](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-pdf-exporter/LICENSE)
|
* `xl-pdf-exporter`: [AGPL-3.0](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-pdf-exporter/LICENSE),
|
||||||
|
* `xl-multi-column`: [AGPL-3.0](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-multi-column/LICENSE).
|
||||||
|
|
||||||
In `.env.development`, `PUBLISH_AS_MIT` is set to `false`, allowing developers to test Docs with all its features.
|
In `.env.development`, `PUBLISH_AS_MIT` is set to `false`, allowing developers to test Docs with all its features.
|
||||||
|
|
||||||
⚠️ If you run Docs in production with `PUBLISH_AS_MIT` set to `false` make sure you fulfill your [BlockNote licensing](https://github.com/TypeCellOS/BlockNote/blob/main/packages/xl-pdf-exporter/LICENSE) or [subscription](https://www.blocknotejs.org/about#partner-with-us) obligations.
|
⚠️ If you run Docs in production with `PUBLISH_AS_MIT` set to `false` make sure you fulfill your BlockNote licensing or [subscription](https://www.blocknotejs.org/about#partner-with-us) obligations.
|
||||||
|
|
||||||
|
|||||||
@@ -346,4 +346,69 @@ test.describe('Doc Export', () => {
|
|||||||
const pdfData = await pdf(pdfBuffer);
|
const pdfData = await pdf(pdfBuffer);
|
||||||
expect(pdfData.text).toContain('Hello World');
|
expect(pdfData.text).toContain('Hello World');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('it exports the doc with multi columns', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
const [randomDoc] = await createDoc(
|
||||||
|
page,
|
||||||
|
'doc-multi-columns',
|
||||||
|
browserName,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.locator('.bn-block-outer').last().fill('/');
|
||||||
|
|
||||||
|
await page.getByText('Three Columns', { exact: true }).click();
|
||||||
|
|
||||||
|
await page.locator('.bn-block-column').first().fill('Column 1');
|
||||||
|
await page.locator('.bn-block-column').nth(1).fill('Column 2');
|
||||||
|
await page.locator('.bn-block-column').last().fill('Column 3');
|
||||||
|
|
||||||
|
expect(await page.locator('.bn-block-column').count()).toBe(3);
|
||||||
|
await expect(
|
||||||
|
page.locator('.bn-block-column[data-node-type="column"]').first(),
|
||||||
|
).toHaveText('Column 1');
|
||||||
|
await expect(
|
||||||
|
page.locator('.bn-block-column[data-node-type="column"]').nth(1),
|
||||||
|
).toHaveText('Column 2');
|
||||||
|
await expect(
|
||||||
|
page.locator('.bn-block-column[data-node-type="column"]').last(),
|
||||||
|
).toHaveText('Column 3');
|
||||||
|
|
||||||
|
await page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'download',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', {
|
||||||
|
name: 'Download',
|
||||||
|
exact: true,
|
||||||
|
}),
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
const downloadPromise = page.waitForEvent('download', (download) => {
|
||||||
|
return download.suggestedFilename().includes(`${randomDoc}.pdf`);
|
||||||
|
});
|
||||||
|
|
||||||
|
void page
|
||||||
|
.getByRole('button', {
|
||||||
|
name: 'Download',
|
||||||
|
exact: true,
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
|
||||||
|
const download = await downloadPromise;
|
||||||
|
expect(download.suggestedFilename()).toBe(`${randomDoc}.pdf`);
|
||||||
|
|
||||||
|
const pdfBuffer = await cs.toBuffer(await download.createReadStream());
|
||||||
|
const pdfData = await pdf(pdfBuffer);
|
||||||
|
expect(pdfData.text).toContain('Column 1');
|
||||||
|
expect(pdfData.text).toContain('Column 2');
|
||||||
|
expect(pdfData.text).toContain('Column 3');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"@blocknote/mantine": "0.34.0",
|
"@blocknote/mantine": "0.34.0",
|
||||||
"@blocknote/react": "0.34.0",
|
"@blocknote/react": "0.34.0",
|
||||||
"@blocknote/xl-docx-exporter": "0.34.0",
|
"@blocknote/xl-docx-exporter": "0.34.0",
|
||||||
|
"@blocknote/xl-multi-column": "0.34.0",
|
||||||
"@blocknote/xl-pdf-exporter": "0.34.0",
|
"@blocknote/xl-pdf-exporter": "0.34.0",
|
||||||
"@dnd-kit/core": "6.3.1",
|
"@dnd-kit/core": "6.3.1",
|
||||||
"@dnd-kit/modifiers": "9.0.0",
|
"@dnd-kit/modifiers": "9.0.0",
|
||||||
|
|||||||
@@ -28,8 +28,13 @@ import { randomColor } from '../utils';
|
|||||||
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
import { BlockNoteSuggestionMenu } from './BlockNoteSuggestionMenu';
|
||||||
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
import { BlockNoteToolbar } from './BlockNoteToolBar/BlockNoteToolbar';
|
||||||
import { CalloutBlock, DividerBlock } from './custom-blocks';
|
import { CalloutBlock, DividerBlock } from './custom-blocks';
|
||||||
|
import XLMultiColumn from './xl-multi-column';
|
||||||
|
|
||||||
export const blockNoteSchema = withPageBreak(
|
const multiColumnDropCursor = XLMultiColumn?.multiColumnDropCursor;
|
||||||
|
const multiColumnLocales = XLMultiColumn?.locales;
|
||||||
|
const withMultiColumn = XLMultiColumn?.withMultiColumn;
|
||||||
|
|
||||||
|
const baseBlockNoteSchema = withPageBreak(
|
||||||
BlockNoteSchema.create({
|
BlockNoteSchema.create({
|
||||||
blockSpecs: {
|
blockSpecs: {
|
||||||
...defaultBlockSpecs,
|
...defaultBlockSpecs,
|
||||||
@@ -39,6 +44,9 @@ export const blockNoteSchema = withPageBreak(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const blockNoteSchema = (withMultiColumn?.(baseBlockNoteSchema) ||
|
||||||
|
baseBlockNoteSchema) as typeof baseBlockNoteSchema;
|
||||||
|
|
||||||
interface BlockNoteEditorProps {
|
interface BlockNoteEditorProps {
|
||||||
doc: Doc;
|
doc: Doc;
|
||||||
provider: HocuspocusProvider;
|
provider: HocuspocusProvider;
|
||||||
@@ -116,7 +124,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
},
|
},
|
||||||
showCursorLabels: showCursorLabels as 'always' | 'activity',
|
showCursorLabels: showCursorLabels as 'always' | 'activity',
|
||||||
},
|
},
|
||||||
dictionary: locales[lang as keyof typeof locales],
|
dictionary: {
|
||||||
|
...locales[lang as keyof typeof locales],
|
||||||
|
multi_column:
|
||||||
|
multiColumnLocales?.[lang as keyof typeof multiColumnLocales],
|
||||||
|
},
|
||||||
tables: {
|
tables: {
|
||||||
splitCells: true,
|
splitCells: true,
|
||||||
cellBackgroundColor: true,
|
cellBackgroundColor: true,
|
||||||
@@ -125,6 +137,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
|
|||||||
},
|
},
|
||||||
uploadFile,
|
uploadFile,
|
||||||
schema: blockNoteSchema,
|
schema: blockNoteSchema,
|
||||||
|
dropCursor: multiColumnDropCursor,
|
||||||
},
|
},
|
||||||
[collabName, lang, provider, uploadFile],
|
[collabName, lang, provider, uploadFile],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import {
|
|||||||
getCalloutReactSlashMenuItems,
|
getCalloutReactSlashMenuItems,
|
||||||
getDividerReactSlashMenuItems,
|
getDividerReactSlashMenuItems,
|
||||||
} from './custom-blocks';
|
} from './custom-blocks';
|
||||||
|
import XLMultiColumn from './xl-multi-column';
|
||||||
|
|
||||||
|
const getMultiColumnSlashMenuItems =
|
||||||
|
XLMultiColumn?.getMultiColumnSlashMenuItems;
|
||||||
|
|
||||||
export const BlockNoteSuggestionMenu = () => {
|
export const BlockNoteSuggestionMenu = () => {
|
||||||
const editor = useBlockNoteEditor<DocsBlockSchema>();
|
const editor = useBlockNoteEditor<DocsBlockSchema>();
|
||||||
@@ -27,8 +31,9 @@ export const BlockNoteSuggestionMenu = () => {
|
|||||||
filterSuggestionItems(
|
filterSuggestionItems(
|
||||||
combineByGroup(
|
combineByGroup(
|
||||||
getDefaultReactSlashMenuItems(editor),
|
getDefaultReactSlashMenuItems(editor),
|
||||||
getPageBreakReactSlashMenuItems(editor),
|
|
||||||
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
|
getCalloutReactSlashMenuItems(editor, t, basicBlocksName),
|
||||||
|
getMultiColumnSlashMenuItems?.(editor) || [],
|
||||||
|
getPageBreakReactSlashMenuItems(editor),
|
||||||
getDividerReactSlashMenuItems(editor, t, basicBlocksName),
|
getDividerReactSlashMenuItems(editor, t, basicBlocksName),
|
||||||
),
|
),
|
||||||
query,
|
query,
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* To import XL modules you must import from the index file.
|
||||||
|
* This is to ensure that the XL modules are only loaded when
|
||||||
|
* the application is not published as MIT.
|
||||||
|
*/
|
||||||
|
import * as XLMultiColumn from '@blocknote/xl-multi-column';
|
||||||
|
|
||||||
|
let modulesXL = undefined;
|
||||||
|
if (process.env.NEXT_PUBLIC_PUBLISH_AS_MIT === 'false') {
|
||||||
|
modulesXL = XLMultiColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModulesXL = typeof XLMultiColumn | undefined;
|
||||||
|
|
||||||
|
export default modulesXL as ModulesXL;
|
||||||
Reference in New Issue
Block a user