@burdenoff/microfe-bigconsole 2026.613.4 → 2026.613.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js +163 -151
- package/dist/bigconsole/components/dashboard/DashboardCanvas.js.map +1 -1
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js +89 -69
- package/dist/bigconsole/components/dashboard/DashboardToolbar.js.map +1 -1
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js +65 -46
- package/dist/bigconsole/components/widgets/map-widget/MapWidget.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableCell.js +50 -30
- package/dist/bigconsole/components/widgets/table/TableCell.js.map +1 -1
- package/dist/bigconsole/components/widgets/table/TableWidget.js +95 -88
- package/dist/bigconsole/components/widgets/table/TableWidget.js.map +1 -1
- package/dist/bigconsole/hooks/useDashboardImageExport.js +92 -1
- package/dist/bigconsole/hooks/useDashboardImageExport.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1,92 @@
|
|
|
1
|
-
import "react";
|
|
1
|
+
import { useCallback as e, useState as t } from "react";
|
|
2
|
+
//#region src/bigconsole/hooks/useDashboardImageExport.ts
|
|
3
|
+
function n(e, t) {
|
|
4
|
+
let n = URL.createObjectURL(e), r = document.createElement("a");
|
|
5
|
+
r.href = n, r.download = t, r.click(), URL.revokeObjectURL(n);
|
|
6
|
+
}
|
|
7
|
+
function r(e) {
|
|
8
|
+
let t = getComputedStyle(e).backgroundColor;
|
|
9
|
+
return !t || t === "transparent" || /,\s*0\)\s*$/.test(t) ? getComputedStyle(document.documentElement).getPropertyValue("--color-bg-surface").trim() || "transparent" : t;
|
|
10
|
+
}
|
|
11
|
+
async function i(e) {
|
|
12
|
+
if (typeof window < "u" && "html2canvas" in window) return await window.html2canvas(e, {
|
|
13
|
+
backgroundColor: r(e),
|
|
14
|
+
scale: 2,
|
|
15
|
+
useCORS: !0
|
|
16
|
+
});
|
|
17
|
+
let t = document.createElement("canvas"), n = t.getContext("2d");
|
|
18
|
+
if (!n) throw Error("Could not get canvas context");
|
|
19
|
+
let i = e.getBoundingClientRect();
|
|
20
|
+
t.width = i.width * 2, t.height = i.height * 2, n.scale(2, 2), n.fillStyle = r(e), n.fillRect(0, 0, i.width, i.height);
|
|
21
|
+
let a = e.querySelectorAll("svg");
|
|
22
|
+
for (let e of a) {
|
|
23
|
+
let t = new XMLSerializer().serializeToString(e), r = new Blob([t], { type: "image/svg+xml;charset=utf-8" }), a = URL.createObjectURL(r);
|
|
24
|
+
try {
|
|
25
|
+
await new Promise((t) => {
|
|
26
|
+
let r = new Image();
|
|
27
|
+
r.onload = () => {
|
|
28
|
+
let a = e.getBoundingClientRect();
|
|
29
|
+
n.drawImage(r, a.left - i.left, a.top - i.top), t();
|
|
30
|
+
}, r.onerror = () => t(), r.src = a;
|
|
31
|
+
});
|
|
32
|
+
} finally {
|
|
33
|
+
URL.revokeObjectURL(a);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return t;
|
|
37
|
+
}
|
|
38
|
+
function a(r) {
|
|
39
|
+
let { containerRef: a, name: o = "dashboard" } = r, [s, c] = t(!1);
|
|
40
|
+
return {
|
|
41
|
+
exportPNG: e(async (e) => {
|
|
42
|
+
let t = a.current;
|
|
43
|
+
if (!(!t || s)) {
|
|
44
|
+
c(!0);
|
|
45
|
+
try {
|
|
46
|
+
let r = await i(t), a = await new Promise((e, t) => {
|
|
47
|
+
r.toBlob((n) => n ? e(n) : t(/* @__PURE__ */ Error("Failed to create image blob")), "image/png");
|
|
48
|
+
}), s = o.replace(/\s+/g, "_").toLowerCase();
|
|
49
|
+
n(a, e || `${s}_board.png`);
|
|
50
|
+
} finally {
|
|
51
|
+
c(!1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}, [
|
|
55
|
+
a,
|
|
56
|
+
o,
|
|
57
|
+
s
|
|
58
|
+
]),
|
|
59
|
+
exportPDF: e(async () => {
|
|
60
|
+
let e = a.current;
|
|
61
|
+
if (!(!e || s)) {
|
|
62
|
+
c(!0);
|
|
63
|
+
try {
|
|
64
|
+
let t = await i(e), n = t.toDataURL("image/png"), r = `${o} — board`, a = window.open("", "_blank");
|
|
65
|
+
if (!a) {
|
|
66
|
+
window.print();
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
let s = t.width >= t.height ? "landscape" : "portrait", c = a.document;
|
|
70
|
+
c.title = r;
|
|
71
|
+
let l = c.createElement("style");
|
|
72
|
+
l.textContent = `@page{size:${s};margin:12mm}html,body{margin:0;padding:0}img{width:100%;height:auto;display:block}`, c.head.appendChild(l);
|
|
73
|
+
let u = c.createElement("img");
|
|
74
|
+
u.alt = r, u.addEventListener("load", () => {
|
|
75
|
+
a.focus(), a.print();
|
|
76
|
+
}), u.src = n, c.body.appendChild(u);
|
|
77
|
+
} finally {
|
|
78
|
+
c(!1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}, [
|
|
82
|
+
a,
|
|
83
|
+
o,
|
|
84
|
+
s
|
|
85
|
+
]),
|
|
86
|
+
isExporting: s
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
//#endregion
|
|
90
|
+
export { a as default };
|
|
91
|
+
|
|
92
|
+
//# sourceMappingURL=useDashboardImageExport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useDashboardImageExport.js","names":[],"sources":["../../../src/bigconsole/hooks/useDashboardImageExport.ts"],"sourcesContent":["/**\n * useDashboardImageExport\n *\n * Exports a whole dashboard board to a single PNG \"board-pack\" image by\n * rasterizing the live board container. Dependency-free: prefers a\n * page-provided `html2canvas` (loaded globally by the host shell when present),\n * otherwise falls back to compositing the board's SVG charts onto a canvas —\n * the same strategy `useWidgetExport` uses for single widgets.\n *\n * `exportPDF` rasterizes the board and prints *just that image* from a new\n * window, so the browser's \"Save as PDF\" produces a clean board-pack PDF with\n * no app chrome. This is intentionally dependency-free: this MFE declares its\n * runtime libs as peerDependencies (provided by the host shell), so bundling a\n * PDF engine here would require host-shell coordination. A library-based vector\n * PDF can be layered on later via the host shell if needed.\n */\n\nimport { useCallback, useState, type RefObject } from 'react';\n\nexport interface UseDashboardImageExportOptions {\n /** Ref to the board/grid container element to rasterize. */\n containerRef: RefObject<HTMLElement | null>;\n /** Human-readable dashboard name, used for the download filename. */\n name?: string;\n}\n\nexport interface UseDashboardImageExportResult {\n /** Export the whole board as a single PNG image. */\n exportPNG: (filename?: string) => Promise<void>;\n /** Export the board as a PDF via the browser's print-to-PDF (board image only). */\n exportPDF: () => Promise<void>;\n isExporting: boolean;\n}\n\nfunction downloadBlob(blob: Blob, filename: string): void {\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = filename;\n a.click();\n URL.revokeObjectURL(url);\n}\n\n/**\n * Resolve a concrete background fill for the export canvas. Prefers the\n * element's own computed background, then the resolved `--color-bg-surface`\n * theme token, then a transparent canvas — no raw color literals.\n */\nfunction resolveCanvasBackground(element: HTMLElement): string {\n const own = getComputedStyle(element).backgroundColor;\n // Treat keyword \"transparent\" or a fully-transparent rgba (alpha 0, i.e. a\n // value ending in \", 0)\") as \"no own background\" and fall back to the token.\n const isTransparent = !own || own === 'transparent' || /,\\s*0\\)\\s*$/.test(own);\n if (!isTransparent) return own;\n const token = getComputedStyle(document.documentElement).getPropertyValue('--color-bg-surface').trim();\n return token || 'transparent';\n}\n\n/** Rasterize an element to a canvas (html2canvas when available, SVG composite otherwise). */\nasync function elementToCanvas(element: HTMLElement): Promise<HTMLCanvasElement> {\n if (typeof window !== 'undefined' && 'html2canvas' in window) {\n // @ts-expect-error - html2canvas is loaded dynamically by the host shell\n return (await window.html2canvas(element, {\n backgroundColor: resolveCanvasBackground(element),\n scale: 2,\n useCORS: true,\n })) as HTMLCanvasElement;\n }\n\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n if (!ctx) throw new Error('Could not get canvas context');\n\n const rect = element.getBoundingClientRect();\n canvas.width = rect.width * 2;\n canvas.height = rect.height * 2;\n ctx.scale(2, 2);\n ctx.fillStyle = resolveCanvasBackground(element);\n ctx.fillRect(0, 0, rect.width, rect.height);\n\n // Composite each SVG chart at its on-screen position.\n const svgs = element.querySelectorAll('svg');\n for (const svg of svgs) {\n const svgData = new XMLSerializer().serializeToString(svg);\n const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });\n const url = URL.createObjectURL(svgBlob);\n try {\n await new Promise<void>((resolve) => {\n const img = new Image();\n img.onload = () => {\n const svgRect = svg.getBoundingClientRect();\n ctx.drawImage(img, svgRect.left - rect.left, svgRect.top - rect.top);\n resolve();\n };\n img.onerror = () => resolve();\n img.src = url;\n });\n } finally {\n URL.revokeObjectURL(url);\n }\n }\n return canvas;\n}\n\nexport function useDashboardImageExport(options: UseDashboardImageExportOptions): UseDashboardImageExportResult {\n const { containerRef, name = 'dashboard' } = options;\n const [isExporting, setIsExporting] = useState(false);\n\n const exportPNG = useCallback(\n async (filename?: string) => {\n const el = containerRef.current;\n if (!el || isExporting) return;\n setIsExporting(true);\n try {\n const canvas = await elementToCanvas(el);\n const blob = await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('Failed to create image blob'))), 'image/png');\n });\n const base = name.replace(/\\s+/g, '_').toLowerCase();\n downloadBlob(blob, filename || `${base}_board.png`);\n } finally {\n setIsExporting(false);\n }\n },\n [containerRef, name, isExporting]\n );\n\n const exportPDF = useCallback(async () => {\n const el = containerRef.current;\n if (!el || isExporting) return;\n setIsExporting(true);\n try {\n const canvas = await elementToCanvas(el);\n const imgData = canvas.toDataURL('image/png');\n const title = `${name} — board`;\n // Print just the board image from an isolated window, so \"Save as PDF\"\n // yields a clean board-pack with no app chrome. Falls back to printing the\n // current page if the popup is blocked. Built via DOM APIs (no\n // document.write) so the user-controlled dashboard `name` can never be\n // interpreted as markup.\n const win = window.open('', '_blank');\n if (!win) {\n window.print();\n return;\n }\n const orientation = canvas.width >= canvas.height ? 'landscape' : 'portrait';\n const doc = win.document;\n doc.title = title; // assigned as text, never parsed as HTML\n const style = doc.createElement('style');\n style.textContent = `@page{size:${orientation};margin:12mm}html,body{margin:0;padding:0}img{width:100%;height:auto;display:block}`;\n doc.head.appendChild(style);\n const img = doc.createElement('img');\n img.alt = title;\n img.addEventListener('load', () => {\n win.focus();\n win.print();\n });\n img.src = imgData; // trusted data: URL from our own canvas\n doc.body.appendChild(img);\n } finally {\n setIsExporting(false);\n }\n }, [containerRef, name, isExporting]);\n\n return { exportPNG, exportPDF, isExporting };\n}\n\nexport default useDashboardImageExport;\n"],"mappings":"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"}
|