From ac10b90f7c6583ea9abb5fdaa351b9ec16c02526 Mon Sep 17 00:00:00 2001 From: Arek Nawo Date: Wed, 15 Jan 2025 13:26:10 +0100 Subject: [PATCH 1/8] feat: Page break block --- examples/01-basic/04-all-blocks/App.tsx | 1 + .../pageBreak/basic/external.html | 1 + .../pageBreak/basic/internal.html | 1 + .../__snapshots__/pageBreak/basic/markdown.md | 0 .../nodeConversions.test.ts.snap | 16 +++ .../src/api/testUtil/cases/defaultSchema.ts | 8 ++ .../PageBreakBlockContent.tsx | 48 +++++++ packages/core/src/blocks/defaultBlocks.ts | 2 + packages/core/src/editor/Block.css | 16 ++- .../getDefaultSlashMenuItems.ts | 12 ++ packages/core/src/i18n/locales/ar.ts | 6 + packages/core/src/i18n/locales/de.ts | 6 + packages/core/src/i18n/locales/en.ts | 6 + packages/core/src/i18n/locales/es.ts | 6 + packages/core/src/i18n/locales/fr.ts | 6 + packages/core/src/i18n/locales/hr.ts | 126 ++++++++++-------- packages/core/src/i18n/locales/is.ts | 6 + packages/core/src/i18n/locales/ja.ts | 6 + packages/core/src/i18n/locales/ko.ts | 6 + packages/core/src/i18n/locales/nl.ts | 6 + packages/core/src/i18n/locales/pl.ts | 6 + packages/core/src/i18n/locales/pt.ts | 6 + packages/core/src/i18n/locales/ru.ts | 6 + packages/core/src/i18n/locales/vi.ts | 6 + packages/core/src/i18n/locales/zh.ts | 6 + packages/core/src/index.ts | 1 + .../getDefaultReactSlashMenuItems.tsx | 2 + shared/testDocument.ts | 1 + 28 files changed, 264 insertions(+), 55 deletions(-) create mode 100644 packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/external.html create mode 100644 packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/internal.html create mode 100644 packages/core/src/api/exporters/markdown/__snapshots__/pageBreak/basic/markdown.md create mode 100644 packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx diff --git a/examples/01-basic/04-all-blocks/App.tsx b/examples/01-basic/04-all-blocks/App.tsx index 6a34751b75..85a191c4e7 100644 --- a/examples/01-basic/04-all-blocks/App.tsx +++ b/examples/01-basic/04-all-blocks/App.tsx @@ -118,6 +118,7 @@ export default function App() { ], }, }, + { type: "pageBreak" }, { type: "file", }, diff --git a/packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/external.html b/packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/external.html new file mode 100644 index 0000000000..ba65bb0620 --- /dev/null +++ b/packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/external.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/internal.html b/packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/internal.html new file mode 100644 index 0000000000..f4303a5ad1 --- /dev/null +++ b/packages/core/src/api/exporters/html/__snapshots__/pageBreak/basic/internal.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/packages/core/src/api/exporters/markdown/__snapshots__/pageBreak/basic/markdown.md b/packages/core/src/api/exporters/markdown/__snapshots__/pageBreak/basic/markdown.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap b/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap index 4b6cebdb80..ef16974ad5 100644 --- a/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +++ b/packages/core/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap @@ -1605,6 +1605,22 @@ exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert } `; +exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert pageBreak/basic to/from prosemirror 1`] = ` +{ + "attrs": { + "backgroundColor": "default", + "id": "1", + "textColor": "default", + }, + "content": [ + { + "type": "pageBreak", + }, + ], + "type": "blockContainer", +} +`; + exports[`Test BlockNote-Prosemirror conversion > Case: default schema > Convert paragraph/basic to/from prosemirror 1`] = ` { "attrs": { diff --git a/packages/core/src/api/testUtil/cases/defaultSchema.ts b/packages/core/src/api/testUtil/cases/defaultSchema.ts index 2510f4e040..53478e2e4c 100644 --- a/packages/core/src/api/testUtil/cases/defaultSchema.ts +++ b/packages/core/src/api/testUtil/cases/defaultSchema.ts @@ -202,6 +202,14 @@ export const defaultSchemaTestCases: EditorTestCases< }, ], }, + { + name: "pageBreak/basic", + blocks: [ + { + type: "pageBreak", + }, + ], + }, { name: "file/button", blocks: [ diff --git a/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx b/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx new file mode 100644 index 0000000000..1ba43ae167 --- /dev/null +++ b/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx @@ -0,0 +1,48 @@ +import { + createBlockSpec, + CustomBlockConfig, + Props, +} from "../../schema/index.js"; + +export const pageBreakConfig = { + type: "pageBreak" as const, + propSchema: {}, + content: "none", + isFileBlock: false, +} satisfies CustomBlockConfig; +export const pageBreakRender = () => { + const pageBreak = document.createElement("div"); + + pageBreak.className = "bn-page-break"; + pageBreak.setAttribute("data-page-break", ""); + + return { + dom: pageBreak, + }; +}; +export const pageBreakParse = ( + element: HTMLElement +): Partial> | undefined => { + if (element.tagName === "DIV" && element.hasAttribute("data-page-break")) { + return { + type: "pageBreak", + }; + } + + return undefined; +}; +export const pageBreakToExternalHTML = () => { + const pageBreak = document.createElement("div"); + + pageBreak.setAttribute("data-page-break", ""); + + return { + dom: pageBreak, + }; +}; + +export const PageBreak = createBlockSpec(pageBreakConfig, { + render: pageBreakRender, + parse: pageBreakParse, + toExternalHTML: pageBreakToExternalHTML, +}); diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts index 81dc0e49ab..d21d74c61d 100644 --- a/packages/core/src/blocks/defaultBlocks.ts +++ b/packages/core/src/blocks/defaultBlocks.ts @@ -22,6 +22,7 @@ import { import { AudioBlock } from "./AudioBlockContent/AudioBlockContent.js"; import { CodeBlock } from "./CodeBlockContent/CodeBlockContent.js"; +import { PageBreak } from "./PageBreakBlockContent/PageBreakBlockContent.js"; import { FileBlock } from "./FileBlockContent/FileBlockContent.js"; import { Heading } from "./HeadingBlockContent/HeadingBlockContent.js"; import { ImageBlock } from "./ImageBlockContent/ImageBlockContent.js"; @@ -38,6 +39,7 @@ export const defaultBlockSpecs = { paragraph: Paragraph, heading: Heading, codeBlock: CodeBlock, + pageBreak: PageBreak, bulletListItem: BulletListItem, numberedListItem: NumberedListItem, checkListItem: CheckListItem, diff --git a/packages/core/src/editor/Block.css b/packages/core/src/editor/Block.css index 54849c2b6c..45628cbb70 100644 --- a/packages/core/src/editor/Block.css +++ b/packages/core/src/editor/Block.css @@ -308,6 +308,20 @@ NESTED BLOCKS transition-delay: 0.1s; } +/* PAGE BREAK */ +.bn-block-content[data-content-type="pageBreak"] > div { + width: 100%; + height: 8px; + background: linear-gradient( + to right, + rgb(22 22 22) 50%, + rgb(255, 255, 255, 0) 0% + ); + background-position: center; + background-size: 16px 1.5px; + background-repeat: repeat-x; +} + /* FILES */ /* Element that wraps content for all file blocks */ @@ -336,7 +350,7 @@ NESTED BLOCKS .bn-editor[contenteditable="true"] [data-file-block] .bn-add-file-button:hover, [data-file-block] .bn-file-name-with-icon:hover, -.ProseMirror-selectednode .bn-file-name-with-icon{ +.ProseMirror-selectednode .bn-file-name-with-icon { background-color: rgb(225, 225, 225); } diff --git a/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts b/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts index fb6fbad7ec..481221c392 100644 --- a/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +++ b/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts @@ -186,6 +186,18 @@ export function getDefaultSlashMenuItems< }); } + if (checkDefaultBlockTypeInSchema("pageBreak", editor)) { + items.push({ + onItemClick: () => { + insertOrUpdateBlock(editor, { + type: "pageBreak", + }); + }, + key: "page_break", + ...editor.dictionary.slash_menu.page_break, + }); + } + if (checkDefaultBlockTypeInSchema("table", editor)) { items.push({ onItemClick: () => { diff --git a/packages/core/src/i18n/locales/ar.ts b/packages/core/src/i18n/locales/ar.ts index b18a202599..c4c2ad8a8e 100644 --- a/packages/core/src/i18n/locales/ar.ts +++ b/packages/core/src/i18n/locales/ar.ts @@ -58,6 +58,12 @@ export const ar: Dictionary = { aliases: ["كود", "مسبق"], group: "الكتل الأساسية", }, + page_break: { + title: "فاصل الصفحة", + subtext: "فاصل الصفحة", + aliases: ["page", "break", "separator", "فاصل", "الصفحة"], + group: "الكتل الأساسية", + }, table: { title: "جدول", subtext: "يستخدم للجداول", diff --git a/packages/core/src/i18n/locales/de.ts b/packages/core/src/i18n/locales/de.ts index 6331d722e7..f4b9e5f5f0 100644 --- a/packages/core/src/i18n/locales/de.ts +++ b/packages/core/src/i18n/locales/de.ts @@ -56,6 +56,12 @@ export const de = { aliases: ["code", "pre"], group: "Grundlegende blöcke", }, + page_break: { + title: "Seitenumbruch", + subtext: "Seitentrenner", + aliases: ["page", "break", "separator", "seitenumbruch", "trenner"], + group: "Grundlegende Blöcke", + }, table: { title: "Tabelle", subtext: "Tabelle mit editierbaren Zellen", diff --git a/packages/core/src/i18n/locales/en.ts b/packages/core/src/i18n/locales/en.ts index e23d0e2638..442f7a3895 100644 --- a/packages/core/src/i18n/locales/en.ts +++ b/packages/core/src/i18n/locales/en.ts @@ -56,6 +56,12 @@ export const en = { aliases: ["code", "pre"], group: "Basic blocks", }, + page_break: { + title: "Page Break", + subtext: "Page separator", + aliases: ["page", "break", "separator"], + group: "Basic blocks", + }, table: { title: "Table", subtext: "Table with editable cells", diff --git a/packages/core/src/i18n/locales/es.ts b/packages/core/src/i18n/locales/es.ts index 2ffc5587fc..0a8e11ac9f 100644 --- a/packages/core/src/i18n/locales/es.ts +++ b/packages/core/src/i18n/locales/es.ts @@ -55,6 +55,12 @@ export const es = { aliases: ["code", "pre"], group: "Bloques básicos", }, + page_break: { + title: "Salto de página", + subtext: "Separador de página", + aliases: ["page", "break", "separator", "salto", "separador"], + group: "Bloques básicos", + }, table: { title: "Tabla", subtext: "Tabla con celdas editables", diff --git a/packages/core/src/i18n/locales/fr.ts b/packages/core/src/i18n/locales/fr.ts index 2dc28972a9..9f694dadb6 100644 --- a/packages/core/src/i18n/locales/fr.ts +++ b/packages/core/src/i18n/locales/fr.ts @@ -71,6 +71,12 @@ export const fr: Dictionary = { aliases: ["code", "pre"], group: "Blocs de base", }, + page_break: { + title: "Saut de page", + subtext: "Séparateur de page", + aliases: ["page", "break", "separator", "saut", "séparateur"], + group: "Blocs de base", + }, table: { title: "Tableau", subtext: "Utilisé pour les tableaux", diff --git a/packages/core/src/i18n/locales/hr.ts b/packages/core/src/i18n/locales/hr.ts index 5b65fc5468..b7604e6ff8 100644 --- a/packages/core/src/i18n/locales/hr.ts +++ b/packages/core/src/i18n/locales/hr.ts @@ -21,26 +21,38 @@ export const hr = { numbered_list: { title: "Numerirani popis", subtext: "Popis s numeriranim stavkama", - aliases: ["poredaniPopis", "stavkaPopisa", "popis", "numeriraniPopis", "numerirani popis"], + aliases: [ + "poredaniPopis", + "stavkaPopisa", + "popis", + "numeriraniPopis", + "numerirani popis", + ], group: "Osnovni blokovi", }, bullet_list: { title: "Popis s oznakama", subtext: "Popis s grafičkim oznakama", - aliases: ["neporedaniPopis", "stavkaPopisa", "popis", "popisSOznakama", "popis s oznakama"], + aliases: [ + "neporedaniPopis", + "stavkaPopisa", + "popis", + "popisSOznakama", + "popis s oznakama", + ], group: "Osnovni blokovi", }, check_list: { title: "Check lista", subtext: "Popis s kućicama za označavanje", aliases: [ - "neporedaniPopis", - "stavkaPopisa", - "popis", - "popisZaProvjeru", - "check lista", - "označeni popis", - "kućicaZaOznačavanje", + "neporedaniPopis", + "stavkaPopisa", + "popis", + "popisZaProvjeru", + "check lista", + "označeni popis", + "kućicaZaOznačavanje", ], group: "Osnovni blokovi", }, @@ -56,17 +68,23 @@ export const hr = { aliases: ["tablica"], group: "Napredno", }, + page_break: { + title: "Prijelom stranice", + subtext: "Razdjelnik stranice", + aliases: ["page", "break", "separator", "prijelom", "razdjelnik"], + group: "Osnovni blokovi", + }, image: { title: "Slika", subtext: "Slika s podesivom veličinom i natpisom", aliases: [ - "slika", - "učitavanjeSlike", - "učitaj", - "img", - "fotografija", - "medij", - "url", + "slika", + "učitavanjeSlike", + "učitaj", + "img", + "fotografija", + "medij", + "url", ], group: "Mediji", }, @@ -74,13 +92,13 @@ export const hr = { title: "Video", subtext: "Video s podesivom veličinom i natpisom", aliases: [ - "video", - "učitavanjeVidea", - "učitaj", - "mp4", - "film", - "medij", - "url", + "video", + "učitavanjeVidea", + "učitaj", + "mp4", + "film", + "medij", + "url", ], group: "Mediji", }, @@ -88,13 +106,13 @@ export const hr = { title: "Audio", subtext: "Audio s natpisom", aliases: [ - "audio", - "učitavanjeAudija", - "učitaj", - "mp3", - "zvuk", - "medij", - "url", + "audio", + "učitavanjeAudija", + "učitaj", + "mp3", + "zvuk", + "medij", + "url", ], group: "Mediji", }, @@ -157,16 +175,16 @@ export const hr = { text_title: "Tekst", background_title: "Pozadina", colors: { - default: "Zadano", - gray: "Siva", - brown: "Smeđa", - red: "Crvena", - orange: "Narančasta", - yellow: "Žuta", - green: "Zelena", - blue: "Plava", - purple: "Ljubičasta", - pink: "Ružičasta", + default: "Zadano", + gray: "Siva", + brown: "Smeđa", + red: "Crvena", + orange: "Narančasta", + yellow: "Žuta", + green: "Zelena", + blue: "Plava", + purple: "Ljubičasta", + pink: "Ružičasta", }, }, @@ -240,29 +258,29 @@ export const hr = { file: "Ukloni datoteku", } as Record, }, - file_preview_toggle: { + file_preview_toggle: { tooltip: "Prikaži/sakrij pregled", - }, - nest: { + }, + nest: { tooltip: "Ugnijezdi blok", secondary_tooltip: "Tab", - }, - unnest: { + }, + unnest: { tooltip: "Razgnijezdi blok", secondary_tooltip: "Shift+Tab", - }, - align_left: { + }, + align_left: { tooltip: "Poravnaj tekst lijevo", - }, - align_center: { + }, + align_center: { tooltip: "Poravnaj tekst po sredini", - }, - align_right: { + }, + align_right: { tooltip: "Poravnaj tekst desno", - }, - align_justify: { + }, + align_justify: { tooltip: "Poravnaj tekst obostrano", - }, + }, }, file_panel: { upload: { diff --git a/packages/core/src/i18n/locales/is.ts b/packages/core/src/i18n/locales/is.ts index 07fcb3bfb7..978ba481ba 100644 --- a/packages/core/src/i18n/locales/is.ts +++ b/packages/core/src/i18n/locales/is.ts @@ -50,6 +50,12 @@ export const is: Dictionary = { aliases: ["kóði", "pre"], group: "Grunnblokkar", }, + page_break: { + title: "Síðubrot", + subtext: "Síðuskil", + aliases: ["page", "break", "separator", "síðubrot", "síðuskil"], + group: "Grunnblokkir", + }, table: { title: "Tafla", subtext: "Notað fyrir töflur", diff --git a/packages/core/src/i18n/locales/ja.ts b/packages/core/src/i18n/locales/ja.ts index 2172289b0b..a4c2ff04a4 100644 --- a/packages/core/src/i18n/locales/ja.ts +++ b/packages/core/src/i18n/locales/ja.ts @@ -74,6 +74,12 @@ export const ja: Dictionary = { aliases: ["code", "pre", "コード", "コードブロック"], group: "基本ブロック", }, + page_break: { + title: "改ページ", + subtext: "ページ区切り", + aliases: ["page", "break", "separator", "改ページ", "区切り"], + group: "基本ブロック", + }, table: { title: "表", subtext: "表に使用", diff --git a/packages/core/src/i18n/locales/ko.ts b/packages/core/src/i18n/locales/ko.ts index cc0144481d..0bf8e24a1f 100644 --- a/packages/core/src/i18n/locales/ko.ts +++ b/packages/core/src/i18n/locales/ko.ts @@ -58,6 +58,12 @@ export const ko: Dictionary = { aliases: ["code", "pre"], group: "기본 블록", }, + page_break: { + title: "페이지 나누기", + subtext: "페이지 구분자", + aliases: ["page", "break", "separator", "페이지", "구분자"], + group: "기본 블록", + }, table: { title: "표", subtext: "간단한 표를 추가합니다.", diff --git a/packages/core/src/i18n/locales/nl.ts b/packages/core/src/i18n/locales/nl.ts index 152e640001..25c3426d2f 100644 --- a/packages/core/src/i18n/locales/nl.ts +++ b/packages/core/src/i18n/locales/nl.ts @@ -50,6 +50,12 @@ export const nl: Dictionary = { aliases: ["code", "pre"], group: "Basisblokken", }, + page_break: { + title: "Pagina-einde", + subtext: "Paginascheiding", + aliases: ["page", "break", "separator", "pagina", "scheiding"], + group: "Basisblokken", + }, table: { title: "Tabel", subtext: "Gebruikt voor tabellen", diff --git a/packages/core/src/i18n/locales/pl.ts b/packages/core/src/i18n/locales/pl.ts index fdf53fe591..befa6ace7c 100644 --- a/packages/core/src/i18n/locales/pl.ts +++ b/packages/core/src/i18n/locales/pl.ts @@ -50,6 +50,12 @@ export const pl: Dictionary = { aliases: ["kod", "pre"], group: "Podstawowe bloki", }, + page_break: { + title: "Podział strony", + subtext: "Separator strony", + aliases: ["page", "break", "separator", "podział", "separator"], + group: "Podstawowe bloki", + }, table: { title: "Tabela", subtext: "Używana do tworzenia tabel", diff --git a/packages/core/src/i18n/locales/pt.ts b/packages/core/src/i18n/locales/pt.ts index 0f435b8955..6dd7dab3ca 100644 --- a/packages/core/src/i18n/locales/pt.ts +++ b/packages/core/src/i18n/locales/pt.ts @@ -57,6 +57,12 @@ export const pt: Dictionary = { aliases: ["codigo", "pre"], group: "Blocos básicos", }, + page_break: { + title: "Quebra de página", + subtext: "Separador de página", + aliases: ["page", "break", "separator", "quebra", "separador"], + group: "Blocos básicos", + }, table: { title: "Tabela", subtext: "Usado para tabelas", diff --git a/packages/core/src/i18n/locales/ru.ts b/packages/core/src/i18n/locales/ru.ts index 54afd7aeb2..60309d5d90 100644 --- a/packages/core/src/i18n/locales/ru.ts +++ b/packages/core/src/i18n/locales/ru.ts @@ -75,6 +75,12 @@ export const ru: Dictionary = { aliases: ["code", "pre", "блок кода"], group: "Базовые блоки", }, + page_break: { + title: "Разрыв страницы", + subtext: "Разделитель страницы", + aliases: ["page", "break", "separator", "разрыв", "разделитель"], + group: "Основные блоки", + }, table: { title: "Таблица", subtext: "Используется для таблиц", diff --git a/packages/core/src/i18n/locales/vi.ts b/packages/core/src/i18n/locales/vi.ts index 507146013f..eb1dbf31a2 100644 --- a/packages/core/src/i18n/locales/vi.ts +++ b/packages/core/src/i18n/locales/vi.ts @@ -57,6 +57,12 @@ export const vi: Dictionary = { aliases: ["code", "pre"], group: "Khối cơ bản", }, + page_break: { + title: "Ngắt trang", + subtext: "Phân cách trang", + aliases: ["page", "break", "separator", "ngắt", "phân cách"], + group: "Khối cơ bản", + }, table: { title: "Bảng", subtext: "Sử dụng để tạo bảng", diff --git a/packages/core/src/i18n/locales/zh.ts b/packages/core/src/i18n/locales/zh.ts index 955b9ca098..cfb32c155f 100644 --- a/packages/core/src/i18n/locales/zh.ts +++ b/packages/core/src/i18n/locales/zh.ts @@ -75,6 +75,12 @@ export const zh: Dictionary = { aliases: ["code", "pre", "代码", "预格式"], group: "基础", }, + page_break: { + title: "分页符", + subtext: "页面分隔符", + aliases: ["page", "break", "separator", "分页", "分隔符"], + group: "基础", + }, table: { title: "表格", subtext: "使用表格", diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 7b0115ae47..1a350d2110 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,6 +6,7 @@ export * from "./api/nodeUtil.js"; export * from "./api/testUtil/index.js"; export * from "./blocks/AudioBlockContent/AudioBlockContent.js"; export * from "./blocks/CodeBlockContent/CodeBlockContent.js"; +export * from "./blocks/PageBreakBlockContent/PageBreakBlockContent.js"; export * from "./blocks/FileBlockContent/FileBlockContent.js"; export * from "./blocks/FileBlockContent/helpers/parse/parseEmbedElement.js"; export * from "./blocks/FileBlockContent/helpers/parse/parseFigureElement.js"; diff --git a/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx b/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx index 6e7bc96beb..56bbbc706c 100644 --- a/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx +++ b/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx @@ -21,6 +21,7 @@ import { RiVolumeUpFill, RiCodeBlock, } from "react-icons/ri"; +import { TbPageBreak } from "react-icons/tb"; import { DefaultReactSuggestionItem } from "./types.js"; const icons = { @@ -38,6 +39,7 @@ const icons = { file: RiFile2Line, emoji: RiEmotionFill, code_block: RiCodeBlock, + page_break: TbPageBreak, }; export function getDefaultReactSlashMenuItems< diff --git a/shared/testDocument.ts b/shared/testDocument.ts index 281e5f9d7b..18b1011655 100644 --- a/shared/testDocument.ts +++ b/shared/testDocument.ts @@ -79,6 +79,7 @@ export const testDocument = partialBlocksToBlocksForTesting( type: "codeBlock", content: "Code Block\nLine 2", }, + { type: "pageBreak" }, { type: "bulletListItem", content: From c40c77371acb9706bb090ab954d028bd2803f516 Mon Sep 17 00:00:00 2001 From: Arek Nawo Date: Wed, 15 Jan 2025 15:51:33 +0100 Subject: [PATCH 2/8] feat: Add page break support to PDF & DOCX exporters --- package-lock.json | 39 + .../src/docx/__snapshots__/basic/document.xml | 5 + .../src/docx/defaultSchema/blocks.ts | 6 + packages/xl-pdf-exporter/package.json | 1 + .../src/pdf/__snapshots__/example.jsx | 1450 +++++++++------- .../exampleWithHeaderAndFooter.jsx | 1483 ++++++++++------- .../src/pdf/defaultSchema/blocks.tsx | 3 + .../src/pdf/pdfExporter.test.tsx | 9 +- .../xl-pdf-exporter/src/pdf/pdfExporter.tsx | 10 +- 9 files changed, 1852 insertions(+), 1154 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2957c32180..51479e96d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3322,6 +3322,13 @@ "node": ">=6.9.0" } }, + "node_modules/@base2/pretty-print-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@base2/pretty-print-object/-/pretty-print-object-1.0.1.tgz", + "integrity": "sha512-4iri8i1AqYHJE2DstZYkyEprg6Pq6sKx3xn5FpySk9sNhH7qN2LLlHJCfDTZRILNwQNPD7mATWM0TBui7uC1pA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/@blocknote/ariakit": { "resolved": "packages/ariakit", "link": true @@ -24963,6 +24970,29 @@ "react": "^18.3.1" } }, + "node_modules/react-element-to-jsx-string": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/react-element-to-jsx-string/-/react-element-to-jsx-string-15.0.0.tgz", + "integrity": "sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@base2/pretty-print-object": "1.0.1", + "is-plain-object": "5.0.0", + "react-is": "18.1.0" + }, + "peerDependencies": { + "react": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0", + "react-dom": "^0.14.8 || ^15.0.1 || ^16.0.0 || ^17.0.1 || ^18.0.0" + } + }, + "node_modules/react-element-to-jsx-string/node_modules/react-is": { + "version": "18.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.1.0.tgz", + "integrity": "sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==", + "dev": true, + "license": "MIT" + }, "node_modules/react-github-btn": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/react-github-btn/-/react-github-btn-1.4.0.tgz", @@ -30544,6 +30574,10 @@ "vite-plugin-eslint": "^1.8.1", "vitest": "^2.0.3", "xml-formatter": "^3.6.3" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || >= 19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || >= 19.0.0-rc" } }, "packages/xl-docx-exporter/node_modules/buffer": { @@ -30595,6 +30629,10 @@ "vite": "^5.3.4", "vite-plugin-eslint": "^1.8.1", "vitest": "^2.0.3" + }, + "peerDependencies": { + "react": "^18.0 || ^19.0 || >= 19.0.0-rc", + "react-dom": "^18.0 || ^19.0 || >= 19.0.0-rc" } }, "packages/xl-multi-column/node_modules/cssstyle": { @@ -30825,6 +30863,7 @@ "jest-image-snapshot": "^6.4.0", "pdf-to-img": "^4.2.0", "prettier": "^2.7.1", + "react-element-to-jsx-string": "^15.0.0", "rollup-plugin-webpack-stats": "^0.2.2", "typescript": "^5.0.4", "vite": "^5.3.4", diff --git a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml index 252040fe31..0781aed46e 100644 --- a/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml +++ b/packages/xl-docx-exporter/src/docx/__snapshots__/basic/document.xml @@ -121,6 +121,11 @@ Line 2 + + + + + diff --git a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts index 43bfba9781..aac6ba3b05 100644 --- a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts +++ b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts @@ -11,6 +11,7 @@ import { ExternalHyperlink, IParagraphOptions, ImageRun, + PageBreak, Paragraph, ParagraphChild, ShadingType, @@ -144,6 +145,11 @@ export const docxBlockMappingForDefaultSchema: BlockMapping< // ], }); }, + pageBreak: () => { + return new Paragraph({ + children: [new PageBreak()], + }); + }, image: async (block, exporter) => { const blob = await exporter.resolveFile(block.props.url); const { width, height } = await getImageDimensions(blob); diff --git a/packages/xl-pdf-exporter/package.json b/packages/xl-pdf-exporter/package.json index 4a50afbfea..4e0b753e30 100644 --- a/packages/xl-pdf-exporter/package.json +++ b/packages/xl-pdf-exporter/package.json @@ -61,6 +61,7 @@ "@types/jsdom": "^21.1.7", "@types/react": "^18.0.25", "@types/react-dom": "^18.0.9", + "react-element-to-jsx-string": "^15.0.0", "eslint": "^8.10.0", "jest-image-snapshot": "^6.4.0", "pdf-to-img": "^4.2.0", diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx index e7158793d3..e2fc942658 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx @@ -1,592 +1,906 @@ -
- - - - - - - Welcome to this - - - demo 🙌! - - - - - + + + + + - - - Hello World nested - - - - + - - - - Hello World double nested - - - - - - - - - This paragraph has a background color - - - - - - - Paragraph - - - - - + + + + + - - Heading - - - - - + + Hello World nested + + + + - - Heading right - - - - - - - justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - - codeBlock not implemented - - - + + + + Hello World double nested + + + + + + + + + + + + + This paragraph has a background color + + + + + + + + + Paragraph + + + + + + + - - - - • - - - - - Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + Heading + + + + + + + - + Heading right + + + + + + + + + justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + + codeBlock not implemented + + + + + + + + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + - - - - • - - - - + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - • - - - - + + + Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - 1. - - - - + + + Numbered List Item 1 - - - - - + + + + + + - - - - 2. - - - - + + + Numbered List Item 2 - - - - - + + + + - - - - - 1. - - - - - Numbered List Item Nested 1 - - - - - - - - - 2. - - - - - Numbered List Item Nested 2 - - - - - - - - - 3. - - - - - Numbered List Item Nested funky right - - - - - - - - - 4. - - - - - Numbered List Item Nested funky center - - - - - - - - - - - 1. - - - - - Numbered List Item - - - - - - - - + - - - - - - Check List Item - - - - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - - - + + + Numbered List Item Nested 1 + + + + + + + - - - - - Open file - - - - - - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg - - - - - - - - - - - - + + + Numbered List Item Nested 2 + + + + + + + - - - - - Open video file - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm - - - - - - - + + + Numbered List Item Nested funky right + + + + + + + - - - - - Open audio file - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 - - - - - - - - - + + + Numbered List Item Nested funky center + + + + + + + + + + + + + + + Numbered List Item + + + + + + + + }> + + + Check List Item + + + + + + + + + + + + + + + - - - - - - audio.mp3 - - - - - Audio file caption - - - - - - + + + Open file + + + + + + + + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg + + + + + + + + + + + + + + + + - Inline Content: - - - - - - + + + + Open video file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm + + + + + + + + + - Styled Text - - - - - + + + + Open audio file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 + + + + + + + + + + + + + + - - Link - - - - - - - - - - Table Cell 1 - - - - - Table Cell 2 - - - - - Table Cell 3 - - - - - - - Table Cell 4 - - - - - Table Cell Bold 5 - - - - - Table Cell 6 - - - - - - - Table Cell 7 - - - - - Table Cell 8 - - - - - Table Cell 9 - - - - - - - - - \ No newline at end of file + + + + + audio.mp3 + + + + + Audio file caption + + + + + + + + + Inline Content: + + + + + + + + + Styled Text + + + {' '} + + + + Link + + + + + + + +
+ + + + \ No newline at end of file diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx index fc93b8c7d0..b0e6f03cca 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx @@ -1,602 +1,931 @@ -
- - + + - - - Header - - - - - - - Welcome to this - - - demo 🙌! - - - - - + Header + + + + + + - - - Hello World nested - - - - + - - - - Hello World double nested - - - - - - - - - This paragraph has a background color - - - - - - - Paragraph - - - - - + + + + + - - Heading - - - - - + + Hello World nested + + + + - - Heading right - - - - - - - justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - - codeBlock not implemented - - - + + + + Hello World double nested + + + + + + + + + + + + + This paragraph has a background color + + + + + + + + + Paragraph + + + + + + + - - - - • - - - - - Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + Heading + + + + + + + - + Heading right + + + + + + + + + justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + + codeBlock not implemented + + + + + + + + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + - - - - • - - - - + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - • - - - - + + + Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - 1. - - - - + + + Numbered List Item 1 - - - - - + + + + + + - - - - 2. - - - - + + + Numbered List Item 2 - - - - - + + + + - - - - - 1. - - - - - Numbered List Item Nested 1 - - - - - - - - - 2. - - - - - Numbered List Item Nested 2 - - - - - - - - - 3. - - - - - Numbered List Item Nested funky right - - - - - - - - - 4. - - - - - Numbered List Item Nested funky center - - - - - - - - - - - 1. - - - - - Numbered List Item - - - - - - - - + - - - - - - Check List Item - - - - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - - - + + + Numbered List Item Nested 1 + + + + + + + - - - - - Open file - - - - - - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg - - - - - - - - - - - - + + + Numbered List Item Nested 2 + + + + + + + - - - - - Open video file - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm - - - - - - - + + + Numbered List Item Nested funky right + + + + + + + - - - - - Open audio file - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 - - - - - - - - - + + + Numbered List Item Nested funky center + + + + + + + + + + + + + + + Numbered List Item + + + + + + + + }> + + + Check List Item + + + + + + + +
+ + + + + + + - - - - - - audio.mp3 - - - - - Audio file caption - - - - - - + + + Open file + + + + + + + + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg + + + + + + + + + + + + + + + + - Inline Content: - - - - - - + + + + Open video file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm + + + + + + + + + - Styled Text - - - - - + + + + Open audio file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 + + + + + + + + + + + + + + - - Link - - - - - - - - - - Table Cell 1 - - - - - Table Cell 2 - - - - - Table Cell 3 - - - - - - - Table Cell 4 - - - - - Table Cell Bold 5 - - - - - Table Cell 6 - - - - - - - Table Cell 7 - - - - - Table Cell 8 - - - - - Table Cell 9 - - - - - - - - - Footer - - - - - \ No newline at end of file + + + + + audio.mp3 + + + + + Audio file caption + + + + + + + + + Inline Content: + + + + + + + + + Styled Text + + + {' '} + + + + Link + + + + + + + +
+ + + + + Footer + + + + \ No newline at end of file diff --git a/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx b/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx index 400422d28d..537c5679b5 100644 --- a/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx +++ b/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx @@ -71,6 +71,9 @@ export const pdfBlockMappingForDefaultSchema: BlockMapping< codeBlock: (block) => { return {block.type + " not implemented"}; }, + pageBreak: () => { + return ; + }, audio: (block, exporter) => { return ( diff --git a/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx b/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx index e21fed3ab7..52b62c40e4 100644 --- a/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx +++ b/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx @@ -9,7 +9,7 @@ import { } from "@blocknote/core"; import { Text } from "@react-pdf/renderer"; import { testDocument } from "@shared/testDocument.js"; -import { prettyDOM, render } from "@testing-library/react"; +import reactElementToJSXString from "react-element-to-jsx-string"; import { describe, expect, it } from "vitest"; import { pdfDefaultSchemaMappings } from "./defaultSchema/index.js"; import { PDFExporter } from "./pdfExporter.js"; @@ -160,8 +160,8 @@ describe("exporter", () => { ); const transformed = await exporter.toReactPDFDocument(testDocument); - const view = render(transformed); - const str = prettyDOM(view.container, undefined, { highlight: false }); + const str = reactElementToJSXString(transformed); + expect(str).toMatchFileSnapshot("__snapshots__/example.jsx"); // would be nice to compare pdf images, but currently doesn't work on mac os (due to node canvas installation issue) @@ -194,8 +194,7 @@ describe("exporter", () => { header: Header, footer: Footer, }); - const view = render(transformed); - const str = prettyDOM(view.container, undefined, { highlight: false }); + const str = reactElementToJSXString(transformed); expect(str).toMatchFileSnapshot( "__snapshots__/exampleWithHeaderAndFooter.jsx" ); diff --git a/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx b/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx index 969f1ce759..db795792a2 100644 --- a/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx +++ b/packages/xl-pdf-exporter/src/pdf/pdfExporter.tsx @@ -63,7 +63,6 @@ export class PDFExporter< fontSize: FONT_SIZE * PIXELS_PER_POINT, // pixels lineHeight: 1.5, }, - section: {}, block: {}, blockChildren: {}, header: {}, @@ -145,6 +144,11 @@ export class PDFExporter< numberedListIndex ); // TODO: any + if (b.type === "pageBreak") { + ret.push(self); + continue; + } + const style = this.blocknoteDefaultPropsToReactPDFStyle(b.props as any); ret.push( <> @@ -247,9 +251,7 @@ export class PDFExporter< {options.header} )} - - {await this.transformBlocks(blocks)} - + {await this.transformBlocks(blocks)} {options.footer && ( Date: Wed, 15 Jan 2025 16:32:21 +0100 Subject: [PATCH 3/8] feat: Add CSS to support Page break when printing --- packages/core/src/editor/Block.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/core/src/editor/Block.css b/packages/core/src/editor/Block.css index 45628cbb70..173f818534 100644 --- a/packages/core/src/editor/Block.css +++ b/packages/core/src/editor/Block.css @@ -322,6 +322,12 @@ NESTED BLOCKS background-repeat: repeat-x; } +@media print { + .bn-block-content[data-content-type="pageBreak"] > div { + page-break-after: always; + } +} + /* FILES */ /* Element that wraps content for all file blocks */ From e42d6c5f3493bd1c00d1b2d5b201351b1b2f25fa Mon Sep 17 00:00:00 2001 From: matthewlipski Date: Fri, 17 Jan 2025 15:35:35 +0100 Subject: [PATCH 4/8] Small UX changes --- .../blocks/PageBreakBlockContent/PageBreakBlockContent.tsx | 1 + packages/core/src/editor/Block.css | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx b/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx index 1ba43ae167..e1e72ab59c 100644 --- a/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx +++ b/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx @@ -9,6 +9,7 @@ export const pageBreakConfig = { propSchema: {}, content: "none", isFileBlock: false, + isSelectable: false, } satisfies CustomBlockConfig; export const pageBreakRender = () => { const pageBreak = document.createElement("div"); diff --git a/packages/core/src/editor/Block.css b/packages/core/src/editor/Block.css index 173f818534..97514fd12d 100644 --- a/packages/core/src/editor/Block.css +++ b/packages/core/src/editor/Block.css @@ -313,9 +313,9 @@ NESTED BLOCKS width: 100%; height: 8px; background: linear-gradient( - to right, - rgb(22 22 22) 50%, - rgb(255, 255, 255, 0) 0% + to right, + rgb(125, 121, 122) 50%, + rgb(125, 121, 122, 0) 0% ); background-position: center; background-size: 16px 1.5px; From b34346d5f863118ceaa6756a778372764fac54ee Mon Sep 17 00:00:00 2001 From: matthewlipski Date: Fri, 17 Jan 2025 16:22:42 +0100 Subject: [PATCH 5/8] Removed page break from default schema --- examples/01-basic/04-all-blocks/App.tsx | 5 ++- .../src/api/testUtil/cases/defaultSchema.ts | 3 +- ...ckContent.tsx => PageBreakBlockContent.ts} | 0 .../getPageBreakSlashMenuItems.ts | 45 +++++++++++++++++++ .../blocks/PageBreakBlockContent/schema.ts | 40 +++++++++++++++++ packages/core/src/blocks/defaultBlocks.ts | 2 - .../SuggestionMenu/DefaultSuggestionItem.ts | 2 +- .../getDefaultSlashMenuItems.ts | 12 ----- packages/core/src/index.ts | 2 + .../getPageBreakReactSlashMenuItems.tsx | 29 ++++++++++++ .../getDefaultReactSlashMenuItems.tsx | 2 - packages/react/src/index.ts | 1 + .../src/docx/defaultSchema/blocks.ts | 3 +- .../src/docx/docxExporter.test.ts | 10 +++-- .../src/pdf/defaultSchema/blocks.tsx | 3 +- .../src/pdf/pdfExporter.test.tsx | 10 ++++- shared/testDocument.ts | 6 ++- 17 files changed, 148 insertions(+), 27 deletions(-) rename packages/core/src/blocks/PageBreakBlockContent/{PageBreakBlockContent.tsx => PageBreakBlockContent.ts} (100%) create mode 100644 packages/core/src/blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.ts create mode 100644 packages/core/src/blocks/PageBreakBlockContent/schema.ts create mode 100644 packages/react/src/blocks/PageBreakBlockContent/getPageBreakReactSlashMenuItems.tsx diff --git a/examples/01-basic/04-all-blocks/App.tsx b/examples/01-basic/04-all-blocks/App.tsx index 85a191c4e7..79dc6bae11 100644 --- a/examples/01-basic/04-all-blocks/App.tsx +++ b/examples/01-basic/04-all-blocks/App.tsx @@ -3,6 +3,7 @@ import { combineByGroup, filterSuggestionItems, locales, + withPageBreak, } from "@blocknote/core"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; @@ -11,6 +12,7 @@ import { SuggestionMenuController, getDefaultReactSlashMenuItems, useCreateBlockNote, + getPageBreakReactSlashMenuItems, } from "@blocknote/react"; import { getMultiColumnSlashMenuItems, @@ -23,7 +25,7 @@ import { useMemo } from "react"; export default function App() { // Creates a new editor instance. const editor = useCreateBlockNote({ - schema: withMultiColumn(BlockNoteSchema.create()), + schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())), dropCursor: multiColumnDropCursor, dictionary: { ...locales.en, @@ -193,6 +195,7 @@ export default function App() { const slashMenuItems = useMemo(() => { return combineByGroup( getDefaultReactSlashMenuItems(editor), + getPageBreakReactSlashMenuItems(editor), getMultiColumnSlashMenuItems(editor) ); }, [editor]); diff --git a/packages/core/src/api/testUtil/cases/defaultSchema.ts b/packages/core/src/api/testUtil/cases/defaultSchema.ts index 53478e2e4c..2e4623bda4 100644 --- a/packages/core/src/api/testUtil/cases/defaultSchema.ts +++ b/packages/core/src/api/testUtil/cases/defaultSchema.ts @@ -6,10 +6,11 @@ import { DefaultInlineContentSchema, DefaultStyleSchema, } from "../../../blocks/defaultBlocks.js"; +import { pageBreakSchema } from "../../../blocks/PageBreakBlockContent/schema.js"; import { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js"; export const defaultSchemaTestCases: EditorTestCases< - DefaultBlockSchema, + DefaultBlockSchema & typeof pageBreakSchema.blockSchema, DefaultInlineContentSchema, DefaultStyleSchema > = { diff --git a/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx b/packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts similarity index 100% rename from packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.tsx rename to packages/core/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts diff --git a/packages/core/src/blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.ts b/packages/core/src/blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.ts new file mode 100644 index 0000000000..6c7b10ad80 --- /dev/null +++ b/packages/core/src/blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.ts @@ -0,0 +1,45 @@ +import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js"; +import { DefaultSuggestionItem } from "../../extensions/SuggestionMenu/DefaultSuggestionItem.js"; +import { insertOrUpdateBlock } from "../../extensions/SuggestionMenu/getDefaultSlashMenuItems.js"; +import { + BlockSchema, + InlineContentSchema, + StyleSchema, +} from "../../schema/index.js"; +import { pageBreakSchema } from "./schema.js"; + +export function checkPageBreakBlocksInSchema< + I extends InlineContentSchema, + S extends StyleSchema +>( + editor: BlockNoteEditor +): editor is BlockNoteEditor { + return ( + "pageBreak" in editor.schema.blockSchema && + editor.schema.blockSchema["pageBreak"] === + pageBreakSchema.blockSchema["pageBreak"] + ); +} + +export function getPageBreakSlashMenuItems< + BSchema extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema +>(editor: BlockNoteEditor) { + const items: (Omit & { key: "page_break" })[] = + []; + + if (checkPageBreakBlocksInSchema(editor)) { + items.push({ + ...editor.dictionary.slash_menu.page_break, + onItemClick: () => { + insertOrUpdateBlock(editor, { + type: "pageBreak", + }); + }, + key: "page_break", + }); + } + + return items; +} diff --git a/packages/core/src/blocks/PageBreakBlockContent/schema.ts b/packages/core/src/blocks/PageBreakBlockContent/schema.ts new file mode 100644 index 0000000000..c9b64a59c5 --- /dev/null +++ b/packages/core/src/blocks/PageBreakBlockContent/schema.ts @@ -0,0 +1,40 @@ +import { BlockNoteSchema } from "../../editor/BlockNoteSchema.js"; +import { + BlockSchema, + InlineContentSchema, + StyleSchema, +} from "../../schema/index.js"; +import { PageBreak } from "./PageBreakBlockContent.js"; + +export const pageBreakSchema = BlockNoteSchema.create({ + blockSpecs: { + pageBreak: PageBreak, + }, +}); + +/** + * Adds page break support to the given schema. + */ +export const withPageBreak = < + B extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema +>( + schema: BlockNoteSchema +) => { + return BlockNoteSchema.create({ + blockSpecs: { + ...schema.blockSpecs, + ...pageBreakSchema.blockSpecs, + }, + inlineContentSpecs: schema.inlineContentSpecs, + styleSpecs: schema.styleSpecs, + }) as any as BlockNoteSchema< + // typescript needs some help here + B & { + pageBreak: typeof PageBreak.config; + }, + I, + S + >; +}; diff --git a/packages/core/src/blocks/defaultBlocks.ts b/packages/core/src/blocks/defaultBlocks.ts index d21d74c61d..81dc0e49ab 100644 --- a/packages/core/src/blocks/defaultBlocks.ts +++ b/packages/core/src/blocks/defaultBlocks.ts @@ -22,7 +22,6 @@ import { import { AudioBlock } from "./AudioBlockContent/AudioBlockContent.js"; import { CodeBlock } from "./CodeBlockContent/CodeBlockContent.js"; -import { PageBreak } from "./PageBreakBlockContent/PageBreakBlockContent.js"; import { FileBlock } from "./FileBlockContent/FileBlockContent.js"; import { Heading } from "./HeadingBlockContent/HeadingBlockContent.js"; import { ImageBlock } from "./ImageBlockContent/ImageBlockContent.js"; @@ -39,7 +38,6 @@ export const defaultBlockSpecs = { paragraph: Paragraph, heading: Heading, codeBlock: CodeBlock, - pageBreak: PageBreak, bulletListItem: BulletListItem, numberedListItem: NumberedListItem, checkListItem: CheckListItem, diff --git a/packages/core/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts b/packages/core/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts index f2e11d5ee8..6789b76616 100644 --- a/packages/core/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts +++ b/packages/core/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts @@ -1,7 +1,7 @@ import type { Dictionary } from "../../i18n/dictionary.js"; export type DefaultSuggestionItem = { - key: keyof Dictionary["slash_menu"]; + key: keyof Omit; title: string; onItemClick: () => void; subtext?: string; diff --git a/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts b/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts index 481221c392..fb6fbad7ec 100644 --- a/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +++ b/packages/core/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts @@ -186,18 +186,6 @@ export function getDefaultSlashMenuItems< }); } - if (checkDefaultBlockTypeInSchema("pageBreak", editor)) { - items.push({ - onItemClick: () => { - insertOrUpdateBlock(editor, { - type: "pageBreak", - }); - }, - key: "page_break", - ...editor.dictionary.slash_menu.page_break, - }); - } - if (checkDefaultBlockTypeInSchema("table", editor)) { items.push({ onItemClick: () => { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 1a350d2110..072d375bc5 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -7,6 +7,8 @@ export * from "./api/testUtil/index.js"; export * from "./blocks/AudioBlockContent/AudioBlockContent.js"; export * from "./blocks/CodeBlockContent/CodeBlockContent.js"; export * from "./blocks/PageBreakBlockContent/PageBreakBlockContent.js"; +export * from "./blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.js"; +export * from "./blocks/PageBreakBlockContent/schema.js"; export * from "./blocks/FileBlockContent/FileBlockContent.js"; export * from "./blocks/FileBlockContent/helpers/parse/parseEmbedElement.js"; export * from "./blocks/FileBlockContent/helpers/parse/parseFigureElement.js"; diff --git a/packages/react/src/blocks/PageBreakBlockContent/getPageBreakReactSlashMenuItems.tsx b/packages/react/src/blocks/PageBreakBlockContent/getPageBreakReactSlashMenuItems.tsx new file mode 100644 index 0000000000..00fc1c274a --- /dev/null +++ b/packages/react/src/blocks/PageBreakBlockContent/getPageBreakReactSlashMenuItems.tsx @@ -0,0 +1,29 @@ +import { + BlockNoteEditor, + BlockSchema, + getPageBreakSlashMenuItems, + InlineContentSchema, + StyleSchema, +} from "@blocknote/core"; +import { DefaultReactSuggestionItem } from "../../components/SuggestionMenu/types.js"; +import { TbPageBreak } from "react-icons/tb"; + +const icons = { + page_break: TbPageBreak, +}; + +export function getPageBreakReactSlashMenuItems< + BSchema extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema +>( + editor: BlockNoteEditor +): (Omit & { key: "page_break" })[] { + return getPageBreakSlashMenuItems(editor).map((item) => { + const Icon = icons[item.key]; + return { + ...item, + icon: , + }; + }); +} diff --git a/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx b/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx index 56bbbc706c..6e7bc96beb 100644 --- a/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx +++ b/packages/react/src/components/SuggestionMenu/getDefaultReactSlashMenuItems.tsx @@ -21,7 +21,6 @@ import { RiVolumeUpFill, RiCodeBlock, } from "react-icons/ri"; -import { TbPageBreak } from "react-icons/tb"; import { DefaultReactSuggestionItem } from "./types.js"; const icons = { @@ -39,7 +38,6 @@ const icons = { file: RiFile2Line, emoji: RiEmotionFill, code_block: RiCodeBlock, - page_break: TbPageBreak, }; export function getDefaultReactSlashMenuItems< diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 579f6bad1e..dd8215d700 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -15,6 +15,7 @@ export * from "./blocks/FileBlockContent/helpers/toExternalHTML/FigureWithCaptio export * from "./blocks/FileBlockContent/helpers/toExternalHTML/LinkWithCaption.js"; export * from "./blocks/FileBlockContent/useResolveUrl.js"; export * from "./blocks/ImageBlockContent/ImageBlockContent.js"; +export * from "./blocks/PageBreakBlockContent/getPageBreakReactSlashMenuItems.js"; export * from "./blocks/VideoBlockContent/VideoBlockContent.js"; export * from "./components/FormattingToolbar/DefaultButtons/BasicTextStyleButton.js"; diff --git a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts index 956c93dc75..1b457f3c35 100644 --- a/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts +++ b/packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts @@ -3,6 +3,7 @@ import { COLORS_DEFAULT, DefaultBlockSchema, DefaultProps, + pageBreakSchema, StyledText, UnreachableCaseError, } from "@blocknote/core"; @@ -57,7 +58,7 @@ function blockPropsToStyles( }; } export const docxBlockMappingForDefaultSchema: BlockMapping< - DefaultBlockSchema, + DefaultBlockSchema & typeof pageBreakSchema.blockSchema, any, any, | Promise diff --git a/packages/xl-docx-exporter/src/docx/docxExporter.test.ts b/packages/xl-docx-exporter/src/docx/docxExporter.test.ts index 42f7449006..ec2d3d9eef 100644 --- a/packages/xl-docx-exporter/src/docx/docxExporter.test.ts +++ b/packages/xl-docx-exporter/src/docx/docxExporter.test.ts @@ -1,4 +1,4 @@ -import { BlockNoteSchema } from "@blocknote/core"; +import { BlockNoteSchema, defaultBlockSpecs, PageBreak } from "@blocknote/core"; import { testDocument } from "@shared/testDocument.js"; import AdmZip from "adm-zip"; import { Packer, Paragraph, TextRun } from "docx"; @@ -10,7 +10,9 @@ import { DOCXExporter } from "./docxExporter.js"; describe("exporter", () => { it("should export a document", { timeout: 10000 }, async () => { const exporter = new DOCXExporter( - BlockNoteSchema.create(), + BlockNoteSchema.create({ + blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak }, + }), docxDefaultSchemaMappings ); const doc = await exporter.toDocxJsDocument(testDocument); @@ -33,7 +35,9 @@ describe("exporter", () => { { timeout: 10000 }, async () => { const exporter = new DOCXExporter( - BlockNoteSchema.create(), + BlockNoteSchema.create({ + blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak }, + }), docxDefaultSchemaMappings ); diff --git a/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx b/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx index 266f5d9d5b..9e75c2d273 100644 --- a/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx +++ b/packages/xl-pdf-exporter/src/pdf/defaultSchema/blocks.tsx @@ -2,6 +2,7 @@ import { BlockMapping, DefaultBlockSchema, DefaultProps, + pageBreakSchema, StyledText, } from "@blocknote/core"; import { Image, Link, Path, Svg, Text, View } from "@react-pdf/renderer"; @@ -17,7 +18,7 @@ const PIXELS_PER_POINT = 0.75; const FONT_SIZE = 16; export const pdfBlockMappingForDefaultSchema: BlockMapping< - DefaultBlockSchema, + DefaultBlockSchema & typeof pageBreakSchema.blockSchema, any, any, React.ReactElement, diff --git a/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx b/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx index 52b62c40e4..0d7df11349 100644 --- a/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx +++ b/packages/xl-pdf-exporter/src/pdf/pdfExporter.test.tsx @@ -6,6 +6,7 @@ import { defaultBlockSpecs, defaultInlineContentSpecs, defaultStyleSpecs, + PageBreak, } from "@blocknote/core"; import { Text } from "@react-pdf/renderer"; import { testDocument } from "@shared/testDocument.js"; @@ -26,6 +27,7 @@ describe("exporter", () => { const schema = BlockNoteSchema.create({ blockSpecs: { ...defaultBlockSpecs, + pageBreak: PageBreak, extraBlock: createBlockSpec( { content: "none", @@ -155,7 +157,9 @@ describe("exporter", () => { it("should export a document", async () => { const exporter = new PDFExporter( - BlockNoteSchema.create(), + BlockNoteSchema.create({ + blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak }, + }), pdfDefaultSchemaMappings ); @@ -186,7 +190,9 @@ describe("exporter", () => { it("should export a document with header and footer", async () => { const exporter = new PDFExporter( - BlockNoteSchema.create(), + BlockNoteSchema.create({ + blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak }, + }), pdfDefaultSchemaMappings ); diff --git a/shared/testDocument.ts b/shared/testDocument.ts index 5194fef071..ccee204029 100644 --- a/shared/testDocument.ts +++ b/shared/testDocument.ts @@ -1,10 +1,14 @@ import { BlockNoteSchema, + defaultBlockSpecs, + PageBreak, partialBlocksToBlocksForTesting, } from "@blocknote/core"; export const testDocument = partialBlocksToBlocksForTesting( - BlockNoteSchema.create(), + BlockNoteSchema.create({ + blockSpecs: { ...defaultBlockSpecs, pageBreak: PageBreak }, + }), [ { type: "paragraph", From 5100f2ab1e6238750ff816dd30930b7a2620cc1a Mon Sep 17 00:00:00 2001 From: matthewlipski Date: Fri, 17 Jan 2025 16:37:49 +0100 Subject: [PATCH 6/8] Fixed unit tests --- .../src/api/testUtil/cases/defaultSchema.ts | 7 +- .../src/pdf/__snapshots__/example.jsx | 1491 +++++++++------- .../exampleWithHeaderAndFooter.jsx | 1524 ++++++++++------- 3 files changed, 1848 insertions(+), 1174 deletions(-) diff --git a/packages/core/src/api/testUtil/cases/defaultSchema.ts b/packages/core/src/api/testUtil/cases/defaultSchema.ts index 2e4623bda4..c08803dab8 100644 --- a/packages/core/src/api/testUtil/cases/defaultSchema.ts +++ b/packages/core/src/api/testUtil/cases/defaultSchema.ts @@ -6,8 +6,12 @@ import { DefaultInlineContentSchema, DefaultStyleSchema, } from "../../../blocks/defaultBlocks.js"; -import { pageBreakSchema } from "../../../blocks/PageBreakBlockContent/schema.js"; +import { + pageBreakSchema, + withPageBreak, +} from "../../../blocks/PageBreakBlockContent/schema.js"; import { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js"; +import { BlockNoteSchema } from "../../../editor/BlockNoteSchema"; export const defaultSchemaTestCases: EditorTestCases< DefaultBlockSchema & typeof pageBreakSchema.blockSchema, @@ -17,6 +21,7 @@ export const defaultSchemaTestCases: EditorTestCases< name: "default schema", createEditor: () => { return BlockNoteEditor.create({ + schema: withPageBreak(BlockNoteSchema.create()), uploadFile: uploadToTmpFilesDotOrg_DEV_ONLY, }); }, diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx index 4abe64202d..74a79c1821 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/example.jsx @@ -1,608 +1,935 @@ -
- - - - - - - Welcome to this - - - demo 🙌! - - - - - + + + + + - - - Hello World nested - - - - + - - - - Hello World double nested - - - - - - - - - This paragraph has a background color - - - - - - - Paragraph - - - - - + + + + + - - Heading - - - - - + + Hello World nested + + + + - - Heading right - - - - - - - justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - + + + + Hello World double nested + + + + + + + + + + + + + This paragraph has a background color + + + + + + + + + Paragraph + + + + + + + - - - - • - - - - - Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + Heading + + + + + + + - + Heading right + + + + + + + + + justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + + + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + - - - - • - - - - + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - • - - - - + + + Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - 1. - - - - + + + Numbered List Item 1 - - - - - + + + + + + - - - - 2. - - - - + + + Numbered List Item 2 - - - - - + + + + - - - - - 1. - - - - - Numbered List Item Nested 1 - - - - - - - - - 2. - - - - - Numbered List Item Nested 2 - - - - - - - - - 3. - - - - - Numbered List Item Nested funky right - - - - - - - - - 4. - - - - - Numbered List Item Nested funky center - - - - - - - - - - - 1. - - - - - Numbered List Item - - - - - - - - + - - - - - - Check List Item - - - - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - - - + + + Numbered List Item Nested 1 + + + + + + + - - - - - Open file - - - - - - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg - - - - - - - - - - - - + + + Numbered List Item Nested 2 + + + + + + + - - - - - Open video file - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm - - - - - - - + + + Numbered List Item Nested funky right + + + + + + + - - - - - Open audio file - - - - + + + Numbered List Item Nested funky center + + + + + + + + + + + + + + + Numbered List Item + + + + + + + + }> + + + Check List Item + + + + + + + +
+ + + + + + + - From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 - - - - - - - - - + + + + Open file + + + + + + + + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg + + + + + + + + + + + + + + + + - - - - - - audio.mp3 - - - - + + + Open video file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm + + + + + + + + + - Audio file caption - - - - - - + + + + Open audio file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 + + + + + + + + + + + + + + - Inline Content: - - - - + + + + audio.mp3 + + + + + Audio file caption + + + + + + + + + Inline Content: + + + + + + + + + Styled Text + + + {' '} + + + + Link + + + + + + + +
+ + + + + - - - Styled Text - - - - - - - Link - - - - - - - - - - Table Cell 1 - - - - - Table Cell 2 - - - - - Table Cell 3 - - - - - - - Table Cell 4 - - - - - Table Cell Bold 5 - - - - - Table Cell 6 - - - - - - - Table Cell 7 - - - - - Table Cell 8 - - - - - Table Cell 9 - - - - - - - - - const helloWorld = (message) => { - - - console.log("Hello World", message); - - - }; - - - - - - - \ No newline at end of file + {`const helloWorld = (message) => {`} + + + console.log("Hello World", message); + + + {`};`} + + + + + + \ No newline at end of file diff --git a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx index 7f90f62314..3c6a68a2f7 100644 --- a/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx +++ b/packages/xl-pdf-exporter/src/pdf/__snapshots__/exampleWithHeaderAndFooter.jsx @@ -1,618 +1,960 @@ -
- - + + - - - Header - - - - - - - Welcome to this - - - demo 🙌! - - - - - + Header + + + + + + - - - Hello World nested - - - - + - - - - Hello World double nested - - - - - - - - - This paragraph has a background color - - - - - - - Paragraph - - - - - + + + + + - - Heading - - - - - + + Hello World nested + + + + - - Heading right - - - - - - - justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - + + + + Hello World double nested + + + + + + + + + + + + + This paragraph has a background color + + + + + + + + + Paragraph + + + + + + + - - - - • - - - - - Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + Heading + + + + + + + - + Heading right + + + + + + + + + justified paragraph. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + + + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + + + + + - - - - • - - - - + + + Bullet List Item. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - • - - - - + + + Bullet List Item right. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. - - - - - + + + + + + - - - - 1. - - - - + + + Numbered List Item 1 - - - - - + + + + + + - - - - 2. - - - - + + + Numbered List Item 2 - - - - - + + + + - - - - - 1. - - - - - Numbered List Item Nested 1 - - - - - - - - - 2. - - - - - Numbered List Item Nested 2 - - - - - - - - - 3. - - - - - Numbered List Item Nested funky right - - - - - - - - - 4. - - - - - Numbered List Item Nested funky center - - - - - - - - - - - 1. - - - - - Numbered List Item - - - - - - - - + - - - - - - Check List Item - - - - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - Wide Cell - - - - - Table Cell - - - - - Table Cell - - - - - - - - - + + + Numbered List Item Nested 1 + + + + + + + - - - - - Open file - - - - - - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg - - - - - - - - - - - - + + + Numbered List Item Nested 2 + + + + + + + - - - - - Open video file - - - - - From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm - - - - - - - + + + Numbered List Item Nested funky right + + + + + + + - - - - - Open audio file - - - - + + + Numbered List Item Nested funky center + + + + + + + + + + + + + + + Numbered List Item + + + + + + + + }> + + + Check List Item + + + + + + + +
+ + + + + + + - From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 - - - - - - - - - + + + + Open file + + + + + + + + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg + + + + + + + + + + + + + + + + - - - - - - audio.mp3 - - - - + + + Open video file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm + + + + + + + + + - Audio file caption - - - - - - + + + + Open audio file + + + + + From https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3 + + + + + + + + + + + + + + - Inline Content: - - - - + + + + audio.mp3 + + + + + Audio file caption + + + + + + + + + Inline Content: + + + + + + + + + Styled Text + + + {' '} + + + + Link + + + + + + + +
+ + + + + - - - Styled Text - - - - - - - Link - - - - - - - - - - Table Cell 1 - - - - - Table Cell 2 - - - - - Table Cell 3 - - - - - - - Table Cell 4 - - - - - Table Cell Bold 5 - - - - - Table Cell 6 - - - - - - - Table Cell 7 - - - - - Table Cell 8 - - - - - Table Cell 9 - - - - - - - - - const helloWorld = (message) => { - - - console.log("Hello World", message); - - - }; - - - - - - - Footer - - - - - \ No newline at end of file + {`const helloWorld = (message) => {`} + + + console.log("Hello World", message); + + + {`};`} + + + + + + + Footer + + + + \ No newline at end of file From 0bda3e31d405b23703ff0891796a21517653fd40 Mon Sep 17 00:00:00 2001 From: matthewlipski Date: Fri, 17 Jan 2025 16:42:45 +0100 Subject: [PATCH 7/8] Fixed lint --- packages/core/src/api/testUtil/cases/defaultSchema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/api/testUtil/cases/defaultSchema.ts b/packages/core/src/api/testUtil/cases/defaultSchema.ts index c08803dab8..dfdff62784 100644 --- a/packages/core/src/api/testUtil/cases/defaultSchema.ts +++ b/packages/core/src/api/testUtil/cases/defaultSchema.ts @@ -11,7 +11,7 @@ import { withPageBreak, } from "../../../blocks/PageBreakBlockContent/schema.js"; import { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js"; -import { BlockNoteSchema } from "../../../editor/BlockNoteSchema"; +import { BlockNoteSchema } from "../../../editor/BlockNoteSchema.js"; export const defaultSchemaTestCases: EditorTestCases< DefaultBlockSchema & typeof pageBreakSchema.blockSchema, From 79f5574393f1c7a198f7024f57c0211a38fe1ea6 Mon Sep 17 00:00:00 2001 From: matthewlipski Date: Fri, 17 Jan 2025 18:42:14 +0100 Subject: [PATCH 8/8] Implemented PR feedback --- examples/01-basic/04-all-blocks/App.tsx | 74 +------------------ .../05-converting-blocks-to-pdf/App.tsx | 36 ++++++++- .../06-converting-blocks-to-docx/App.tsx | 35 ++++++++- packages/core/src/editor/Block.css | 11 +-- packages/react/src/editor/styles.css | 4 + 5 files changed, 74 insertions(+), 86 deletions(-) diff --git a/examples/01-basic/04-all-blocks/App.tsx b/examples/01-basic/04-all-blocks/App.tsx index 79dc6bae11..935e96d93e 100644 --- a/examples/01-basic/04-all-blocks/App.tsx +++ b/examples/01-basic/04-all-blocks/App.tsx @@ -1,36 +1,11 @@ -import { - BlockNoteSchema, - combineByGroup, - filterSuggestionItems, - locales, - withPageBreak, -} from "@blocknote/core"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; -import { - SuggestionMenuController, - getDefaultReactSlashMenuItems, - useCreateBlockNote, - getPageBreakReactSlashMenuItems, -} from "@blocknote/react"; -import { - getMultiColumnSlashMenuItems, - multiColumnDropCursor, - locales as multiColumnLocales, - withMultiColumn, -} from "@blocknote/xl-multi-column"; -import { useMemo } from "react"; +import { useCreateBlockNote } from "@blocknote/react"; export default function App() { // Creates a new editor instance. const editor = useCreateBlockNote({ - schema: withMultiColumn(withPageBreak(BlockNoteSchema.create())), - dropCursor: multiColumnDropCursor, - dictionary: { - ...locales.en, - multi_column: multiColumnLocales.en, - }, initialContent: [ { type: "paragraph", @@ -53,35 +28,6 @@ export default function App() { type: "paragraph", content: "Paragraph", }, - { - type: "columnList", - children: [ - { - type: "column", - props: { - width: 0.8, - }, - children: [ - { - type: "paragraph", - content: "Hello to the left!", - }, - ], - }, - { - type: "column", - props: { - width: 1.2, - }, - children: [ - { - type: "paragraph", - content: "Hello to the right!", - }, - ], - }, - ], - }, { type: "heading", content: "Heading", @@ -120,7 +66,6 @@ export default function App() { ], }, }, - { type: "pageBreak" }, { type: "file", }, @@ -192,21 +137,6 @@ export default function App() { ], }); - const slashMenuItems = useMemo(() => { - return combineByGroup( - getDefaultReactSlashMenuItems(editor), - getPageBreakReactSlashMenuItems(editor), - getMultiColumnSlashMenuItems(editor) - ); - }, [editor]); - // Renders the editor instance using a React component. - return ( - - filterSuggestionItems(slashMenuItems, query)} - /> - - ); + return ; } diff --git a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx index 63ba2109aa..269cc81c9a 100644 --- a/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx +++ b/examples/05-interoperability/05-converting-blocks-to-pdf/App.tsx @@ -1,13 +1,25 @@ +import { + BlockNoteSchema, + combineByGroup, + filterSuggestionItems, + withPageBreak, +} from "@blocknote/core"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; -import { useCreateBlockNote } from "@blocknote/react"; +import { + getDefaultReactSlashMenuItems, + getPageBreakReactSlashMenuItems, + SuggestionMenuController, + useCreateBlockNote, +} from "@blocknote/react"; import { PDFExporter, pdfDefaultSchemaMappings, } from "@blocknote/xl-pdf-exporter"; import { PDFViewer } from "@react-pdf/renderer"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; + import "./styles.css"; export default function App() { @@ -16,6 +28,7 @@ export default function App() { // Creates a new editor instance with some initial content. const editor = useCreateBlockNote({ + schema: withPageBreak(BlockNoteSchema.create()), initialContent: [ { type: "paragraph", @@ -180,6 +193,9 @@ export default function App() { ], }, }, + { + type: "pageBreak", + }, { type: "file", }, @@ -308,11 +324,25 @@ export default function App() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const slashMenuItems = useMemo(() => { + return combineByGroup( + getDefaultReactSlashMenuItems(editor), + getPageBreakReactSlashMenuItems(editor) + ); + }, [editor]); + // Renders the editor instance, and its contents as HTML below. return (
- + + + filterSuggestionItems(slashMenuItems, query) + } + /> +
{pdfDocument} diff --git a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx index a456d84f94..94bb8d8c91 100644 --- a/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx +++ b/examples/05-interoperability/06-converting-blocks-to-docx/App.tsx @@ -1,16 +1,30 @@ +import { + BlockNoteSchema, + combineByGroup, + filterSuggestionItems, + withPageBreak, +} from "@blocknote/core"; import "@blocknote/core/fonts/inter.css"; import { BlockNoteView } from "@blocknote/mantine"; import "@blocknote/mantine/style.css"; -import { useCreateBlockNote } from "@blocknote/react"; +import { + getDefaultReactSlashMenuItems, + getPageBreakReactSlashMenuItems, + SuggestionMenuController, + useCreateBlockNote, +} from "@blocknote/react"; import { DOCXExporter, docxDefaultSchemaMappings, } from "@blocknote/xl-docx-exporter"; +import { useMemo } from "react"; + import "./styles.css"; export default function App() { // Creates a new editor instance with some initial content. const editor = useCreateBlockNote({ + schema: withPageBreak(BlockNoteSchema.create()), initialContent: [ { type: "paragraph", @@ -175,6 +189,9 @@ export default function App() { ], }, }, + { + type: "pageBreak", + }, { type: "file", }, @@ -305,6 +322,13 @@ export default function App() { window.URL.revokeObjectURL(link.href); }; + const slashMenuItems = useMemo(() => { + return combineByGroup( + getDefaultReactSlashMenuItems(editor), + getPageBreakReactSlashMenuItems(editor) + ); + }, [editor]); + // Renders the editor instance, and its contents as HTML below. return (
@@ -314,7 +338,14 @@ export default function App() {
- + + + filterSuggestionItems(slashMenuItems, query) + } + /> +
); diff --git a/packages/core/src/editor/Block.css b/packages/core/src/editor/Block.css index 97514fd12d..284cbeb071 100644 --- a/packages/core/src/editor/Block.css +++ b/packages/core/src/editor/Block.css @@ -311,15 +311,8 @@ NESTED BLOCKS /* PAGE BREAK */ .bn-block-content[data-content-type="pageBreak"] > div { width: 100%; - height: 8px; - background: linear-gradient( - to right, - rgb(125, 121, 122) 50%, - rgb(125, 121, 122, 0) 0% - ); - background-position: center; - background-size: 16px 1.5px; - background-repeat: repeat-x; + height: 0; + border-top: dotted rgb(125, 121, 122) 4px; } @media print { diff --git a/packages/react/src/editor/styles.css b/packages/react/src/editor/styles.css index 2ac35161d8..1f1b3c25ad 100644 --- a/packages/react/src/editor/styles.css +++ b/packages/react/src/editor/styles.css @@ -238,3 +238,7 @@ .bn-side-menu[data-url="false"] { height: 54px; } + +.bn-side-menu[data-block-type="pageBreak"] { + transform: translateY(-10px); +}