UNPKG

@blocknote/xl-docx-exporter

Version:

A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.

475 lines (474 loc) 13.1 kB
import { mapTableCell as k, UnreachableCaseError as T, Exporter as v, COLORS_DEFAULT as F } from "@blocknote/core"; import { Table as D, TableRow as I, TableCell as g, Paragraph as l, ShadingType as w, ImageRun as S, PageBreak as _, TextRun as p, CheckBox as M, ExternalHyperlink as L, Tab as E, AlignmentType as C, LevelFormat as A, Packer as U, Document as R } from "docx"; async function H(e) { if (typeof window < "u") { const t = await createImageBitmap(e), { width: n, height: r } = t; return t.close(), { width: n, height: r }; } else { const t = (await import("image-meta")).imageMeta, n = new Uint8Array(await e.arrayBuffer()), r = t(n); if (!r.width || !r.height) throw new Error("Image dimensions not found"); return { width: r.width, height: r.height }; } } const W = (e, t) => { const r = new Array(e.headerRows ?? 0).fill(!0), o = new Array(e.headerCols ?? 0).fill(!0); return new D({ layout: "autofit", columnWidths: e.columnWidths.map( (i) => (i ?? 120) * /* to points */ 0.75 * /* to twips */ 20 ), rows: e.rows.map((i, u) => { const c = r[u]; return new I({ tableHeader: c, children: i.cells.map((d, y) => { var b; const x = (b = e.columnWidths) == null ? void 0 : b[y], s = k(d), O = o[y]; return new g({ width: x ? { size: `${x * 0.75}pt`, type: "dxa" } : void 0, columnSpan: s.props.colspan, rowSpan: s.props.rowspan, shading: s.props.backgroundColor === "default" || !s.props.backgroundColor ? void 0 : { type: w.SOLID, color: t.options.colors[s.props.backgroundColor].background.slice(1) }, children: [ new l({ children: t.transformInlineContent(s.content), alignment: !s.props.textAlignment || s.props.textAlignment === "left" ? void 0 : s.props.textAlignment === "center" ? "center" : s.props.textAlignment === "right" ? "right" : s.props.textAlignment === "justify" ? "distribute" : (() => { throw new T( s.props.textAlignment ); })(), run: { // TODO add support for table headers exporting, bolding seems to not be working at the moment bold: c || O, // TODO table paragraph color seems to not be working at the moment // Probably because the runs are setting their own color color: s.props.textColor === "default" || !s.props.textColor ? void 0 : t.options.colors[s.props.textColor].text.slice(1) } }) ] }); }) }); }) }); }; function a(e, t) { return { shading: e.backgroundColor === "default" || !e.backgroundColor ? void 0 : { type: w.SOLID, color: t[e.backgroundColor].background.slice(1) }, run: e.textColor === "default" || !e.textColor ? void 0 : { color: t[e.textColor].text.slice(1) }, alignment: !e.textAlignment || e.textAlignment === "left" ? void 0 : e.textAlignment === "center" ? "center" : e.textAlignment === "right" ? "right" : e.textAlignment === "justify" ? "distribute" : (() => { throw new T(e.textAlignment); })() }; } const $ = { paragraph: (e, t) => new l({ ...a(e.props, t.options.colors), children: t.transformInlineContent(e.content), style: "Normal", run: { font: "Inter" } }), toggleListItem: (e, t) => new l({ ...a(e.props, t.options.colors), children: [ new p({ children: ["> "] }), ...t.transformInlineContent(e.content) ] }), numberedListItem: (e, t, n) => new l({ ...a(e.props, t.options.colors), children: t.transformInlineContent(e.content), numbering: { reference: "blocknote-numbered-list", level: n } }), bulletListItem: (e, t, n) => new l({ ...a(e.props, t.options.colors), children: t.transformInlineContent(e.content), numbering: { reference: "blocknote-bullet-list", level: n } }), checkListItem: (e, t) => new l({ ...a(e.props, t.options.colors), children: [ new M({ checked: e.props.checked }), new p({ children: [" "] }), ...t.transformInlineContent(e.content) ] }), heading: (e, t) => new l({ ...a(e.props, t.options.colors), children: t.transformInlineContent(e.content), heading: `Heading${e.props.level}` }), quote: (e, t) => new l({ shading: { color: "#7D797A" }, border: { left: { color: "#7D797A", space: 100, style: "single", size: 8 } }, ...a(e.props, t.options.colors), children: t.transformInlineContent(e.content) }), audio: (e, t) => [ m(e.props, "Open audio", t), ...f(e.props, t) ], video: (e, t) => [ m(e.props, "Open video", t), ...f(e.props, t) ], file: (e, t) => [ m(e.props, "Open file", t), ...f(e.props, t) ], codeBlock: (e) => { var n; const t = ((n = e.content[0]) == null ? void 0 : n.text) || ""; return new l({ style: "Codeblock", shading: { type: w.SOLID, fill: "161616", color: "161616" }, children: [ ...t.split(` `).map((r, o) => new p({ text: r, break: o > 0 ? 1 : 0 })) ] }); }, pageBreak: () => new l({ children: [new _()] }), column: (e, t, n, r, o) => new g({ width: { size: `${e.props.width * 100}%`, type: "pct" }, children: (o || []).flatMap((i) => Array.isArray(i) ? i : [i]) }), columnList: (e, t, n, r, o) => new D({ layout: "autofit", borders: { bottom: { style: "nil" }, top: { style: "nil" }, left: { style: "nil" }, right: { style: "nil" }, insideHorizontal: { style: "nil" }, insideVertical: { style: "nil" } }, rows: [ new I({ children: o.map( (i, u, c) => { var d; return new g({ width: { size: `${parseFloat(`${((d = i.options.width) == null ? void 0 : d.size) || "100%"}`) / (c.length * 100) * 100}%`, type: "pct" }, children: i.options.children }); } ) }) ] }), image: async (e, t) => { const n = await t.resolveFile(e.props.url), { width: r, height: o } = await H(n); return [ new l({ ...a(e.props, t.options.colors), children: [ new S({ data: await n.arrayBuffer(), // it would be nicer to set the actual data type here, but then we'd need to use a mime type / image type // detector. atm passing gif does not seem to be causing issues as the "type" is mainly used by docxjs internally // (i.e.: to make sure it's not svg) type: "gif", altText: e.props.caption ? { description: e.props.caption, name: e.props.caption, title: e.props.caption } : void 0, transformation: { width: e.props.previewWidth || r, height: (e.props.previewWidth || r) / r * o } }) ] }), ...f(e.props, t) ]; }, table: (e, t) => W(e.content, t) }; function m(e, t, n) { return new l({ ...a(e, n.options.colors), children: [ new L({ children: [ new p({ text: e.name || t, style: "Hyperlink" }) ], link: e.url }) ] }); } function f(e, t) { return e.caption ? [ new l({ ...a(e, t.options.colors), children: [ new p({ text: e.caption }) ], style: "Caption" }) ] : []; } const z = { link: (e, t) => new L({ children: e.content.map((n) => t.transformStyledText( n, !0 )), link: e.href }), text: (e, t) => t.transformStyledText(e) }, P = { bold: (e) => e ? { bold: e } : {}, italic: (e) => e ? { italics: e } : {}, underline: (e) => e ? { underline: { type: "single" } } : {}, strike: (e) => e ? { strike: e } : {}, backgroundColor: (e, t) => e ? { shading: { fill: t.options.colors[e].background.slice(1) } } : {}, textColor: (e, t) => e ? { color: t.options.colors[e].text.slice(1) } : {}, code: (e) => e ? { font: "GeistMono" } : {} }, J = { blockMapping: $, inlineContentMapping: z, styleMapping: P }; async function j(e) { return "https://corsproxy.api.blocknotejs.org/corsproxy/?url=" + encodeURIComponent(e); } async function B(e) { { const t = e.default; return await (await fetch(t)).arrayBuffer(); } } const h = ( /* default font size */ 16 * /* 1 pixel is 0.75 points */ 0.75 * /* 1.5em*/ 1.5 * /* 1 point is 20 twips */ 20 ); class q extends v { constructor(t, n, r) { const i = { ...{ colors: F, resolveFileUrl: j }, ...r }; super(t, n, i), this.schema = t, this.mappings = n; } /** * Mostly for internal use, you probably want to use `toBlob` or `toDocxJsDocument` instead. */ transformStyledText(t, n) { const r = this.mapStyles(t.styles), o = Object.assign( {}, ...r ); return new p({ ...o, style: n ? "Hyperlink" : void 0, text: t.text }); } /** * Mostly for internal use, you probably want to use `toBlob` or `toDocxJsDocument` instead. */ async transformBlocks(t, n = 0) { const r = []; for (const o of t) { let i = await this.transformBlocks(o.children, n + 1); ["columnList", "column"].includes(o.type) || (i = i.map((c, d) => (c instanceof l && !c.properties.numberingReferences.length && c.addRunToFront( new p({ children: [new E()] }) ), c))); const u = await this.mapBlock( o, n, 0, i ); ["columnList", "column"].includes(o.type) ? r.push(u) : Array.isArray(u) ? r.push(...u, ...i) : r.push(u, ...i); } return r; } async getFonts() { let t = await B( await import("./Inter_18pt-Regular-byxnNS-8.js") ), n = await B( await import("./GeistMono-Regular-D4rKXxwr.js") ); if (t instanceof ArrayBuffer || n instanceof ArrayBuffer) { const r = (await import("./index-cA_PmnZy.js").then((o) => o.i)).Buffer; t instanceof ArrayBuffer && (t = r.from(t)), n instanceof ArrayBuffer && (n = r.from(n)); } return [ { name: "Inter", data: t }, { name: "GeistMono", data: n } ]; } async createDefaultDocumentOptions() { const t = (await import("./styles-CujW8HHo.js")).default, n = ["•"]; return { numbering: { config: [ { reference: "blocknote-numbered-list", levels: Array.from({ length: 9 }, (r, o) => ({ start: 1, level: o, format: A.DECIMAL, text: `%${o + 1}.`, alignment: C.LEFT, style: { paragraph: { indent: { left: h * (o + 1), hanging: h } } } })) }, { reference: "blocknote-bullet-list", levels: Array.from({ length: 9 }, (r, o) => ({ start: 1, level: o, format: A.BULLET, text: n[o % n.length], alignment: C.LEFT, style: { paragraph: { indent: { left: h * (o + 1), hanging: h } } } })) } ] }, fonts: await this.getFonts(), defaultTabStop: 200, externalStyles: t }; } /** * Convert a document (array of Blocks to a Blob representing a .docx file) */ async toBlob(t, n = { sectionOptions: {}, documentOptions: {} }) { const r = await this.toDocxJsDocument(t, n), o = globalThis.Buffer; try { return globalThis.Buffer || (globalThis.Buffer = (await import("buffer")).default.Buffer), U.toBlob(r); } finally { globalThis.Buffer = o; } } /** * Convert a document (array of Blocks to a docxjs Document) */ async toDocxJsDocument(t, n = { sectionOptions: {}, documentOptions: {} }) { return new R({ ...await this.createDefaultDocumentOptions(), ...n.documentOptions, sections: [ { children: await this.transformBlocks(t), ...n.sectionOptions } ] }); } } export { q as DOCXExporter, J as docxDefaultSchemaMappings }; //# sourceMappingURL=blocknote-xl-docx-exporter.js.map