Wait for all images before printing
This commit is contained in:
parent
c489209610
commit
c72f5fed82
5 changed files with 91 additions and 34 deletions
|
|
@ -260,7 +260,7 @@ console.log(globalThis.COMPILED_FROM_VERSION); // например, "0.3.7"
|
|||
PrintFullDocumentation();
|
||||
```
|
||||
|
||||
Она формирует единый документ из всех страниц в порядке manifest и открывает системный диалог печати. Каждая страница документации начинается с нового печатного листа. Печатная тема всегда использует белый фон и чёрный текст независимо от выбранной экранной темы.
|
||||
Она сразу закрывает интерфейс тематическим loading-overlay, формирует единый документ из всех страниц в порядке manifest, подставляет и декодирует все изображения, ожидает готовности шрифтов и только затем открывает системный диалог печати. Каждая страница документации начинается с нового печатного листа. Печатная тема всегда использует белый фон, чёрный текст и компактные заголовки независимо от выбранной экранной темы.
|
||||
|
||||
## Live-preview
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
"""Single source of truth for the SharedDocsLib package version."""
|
||||
|
||||
__version__ = "0.3.8"
|
||||
__version__ = "0.3.9"
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ button { color: inherit; }
|
|||
|
||||
.mobile-view, .mobile-dock { display: none; }
|
||||
.full-print-document { display: none; }
|
||||
.print-loading-overlay { position: fixed; inset: 0; z-index: 2147483647; display: grid; width: 100vw; height: 100dvh; place-items: center; color: var(--text); background: var(--shell); }
|
||||
.print-loading-spinner { width: 48px; height: 48px; box-sizing: border-box; border: solid 4px currentColor; border-top-color: transparent; border-radius: 9999px; animation: refresh-spin .8s linear infinite; }
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.app-shell { display: block; }
|
||||
|
|
@ -186,7 +188,7 @@ button { color: inherit; }
|
|||
.content a, .content figcaption, .content blockquote { color: #000 !important; }
|
||||
.content pre, .content code, .content details, .content th, .callout { color: #000 !important; background: #fff !important; border-color: #777 !important; }
|
||||
.app-shell { display: block; }
|
||||
.desktop-header, .desktop-sidebar, .mobile-dock, .page-navigation { display: none; }
|
||||
.desktop-header, .desktop-sidebar, .mobile-dock, .page-navigation, .print-loading-overlay { display: none !important; }
|
||||
.workspace, .page-surface { display: block; height: auto; overflow: visible; }
|
||||
.content { width: 100%; padding: 0; }
|
||||
body.docslib-print-all > .app-shell { display: none; }
|
||||
|
|
@ -194,6 +196,9 @@ button { color: inherit; }
|
|||
.print-page { break-after: page; page-break-after: always; }
|
||||
.print-page:last-child { break-after: auto; page-break-after: auto; }
|
||||
.print-page-heading { margin: 0 0 2rem; padding-bottom: 1rem; border-bottom: 1px solid #777; }
|
||||
.print-page-heading h1 { margin: 0; color: #000 !important; }
|
||||
.print-page-heading h1 { margin: 0; color: #000 !important; font-size: 1.35rem !important; line-height: 1.25; }
|
||||
.print-page-number { display: block; margin-bottom: .3rem; color: #555 !important; }
|
||||
.print-page-content h1 { font-size: 1.8rem !important; line-height: 1.25; }
|
||||
.print-page-content h2 { font-size: 1.4rem !important; line-height: 1.3; }
|
||||
.print-page-content h3 { font-size: 1.15rem !important; line-height: 1.35; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,11 +226,14 @@
|
|||
return asset ? `data:${asset.mime};base64,${asset.data}` : "";
|
||||
}
|
||||
|
||||
function hydrateAssets(root = elements.content) {
|
||||
function hydrateAssets(root = elements.content, { eager = false } = {}) {
|
||||
root.querySelectorAll("[data-docslib-asset]").forEach((element) => {
|
||||
const name = element.dataset.docslibAsset;
|
||||
const uri = assetDataUri(name);
|
||||
if (uri && element.tagName === "IMG") element.src = uri;
|
||||
if (uri && element.tagName === "IMG") {
|
||||
if (eager) element.loading = "eager";
|
||||
element.src = uri;
|
||||
}
|
||||
else if (!uri) element.setAttribute("title", name);
|
||||
});
|
||||
}
|
||||
|
|
@ -240,37 +243,76 @@
|
|||
document.getElementById("docslib-full-print")?.remove();
|
||||
}
|
||||
|
||||
function PrintFullDocumentation() {
|
||||
function showPrintLoading() {
|
||||
document.getElementById("docslib-print-loading")?.remove();
|
||||
const overlay = document.createElement("div");
|
||||
overlay.id = "docslib-print-loading";
|
||||
overlay.className = "print-loading-overlay";
|
||||
overlay.setAttribute("aria-hidden", "true");
|
||||
const spinner = document.createElement("div");
|
||||
spinner.className = "print-loading-spinner";
|
||||
overlay.append(spinner);
|
||||
document.body.append(overlay);
|
||||
return overlay;
|
||||
}
|
||||
|
||||
async function waitForPrintAssets(root) {
|
||||
const images = [...root.querySelectorAll("img")];
|
||||
await Promise.all(images.map(async (image) => {
|
||||
if (!image.complete) {
|
||||
await new Promise((resolve) => {
|
||||
image.addEventListener("load", resolve, { once: true });
|
||||
image.addEventListener("error", resolve, { once: true });
|
||||
});
|
||||
}
|
||||
if (typeof image.decode === "function" && image.naturalWidth > 0) {
|
||||
await image.decode().catch(() => {});
|
||||
}
|
||||
}));
|
||||
if (document.fonts && document.fonts.ready) await document.fonts.ready;
|
||||
await new Promise((resolve) => requestAnimationFrame(() => requestAnimationFrame(resolve)));
|
||||
}
|
||||
|
||||
async function PrintFullDocumentation() {
|
||||
const overlay = showPrintLoading();
|
||||
removeFullPrintDocument();
|
||||
const printable = document.createElement("div");
|
||||
printable.id = "docslib-full-print";
|
||||
printable.className = "full-print-document";
|
||||
try {
|
||||
const printable = document.createElement("div");
|
||||
printable.id = "docslib-full-print";
|
||||
printable.className = "full-print-document";
|
||||
|
||||
manifest.pages.forEach((page) => {
|
||||
const article = document.createElement("article");
|
||||
article.className = "content print-page";
|
||||
article.dataset.page = page.number;
|
||||
manifest.pages.forEach((page) => {
|
||||
const article = document.createElement("article");
|
||||
article.className = "content print-page";
|
||||
article.dataset.page = page.number;
|
||||
|
||||
const heading = document.createElement("header");
|
||||
heading.className = "print-page-heading";
|
||||
const number = document.createElement("span");
|
||||
number.className = "print-page-number";
|
||||
number.textContent = page.number;
|
||||
const title = document.createElement("h1");
|
||||
title.textContent = page.title;
|
||||
heading.append(number, title);
|
||||
const heading = document.createElement("header");
|
||||
heading.className = "print-page-heading";
|
||||
const number = document.createElement("span");
|
||||
number.className = "print-page-number";
|
||||
number.textContent = page.number;
|
||||
const title = document.createElement("h1");
|
||||
title.textContent = page.title;
|
||||
heading.append(number, title);
|
||||
|
||||
const body = document.createElement("div");
|
||||
body.className = "print-page-content";
|
||||
body.innerHTML = page.content || "";
|
||||
article.append(heading, body);
|
||||
hydrateAssets(article);
|
||||
printable.append(article);
|
||||
});
|
||||
const body = document.createElement("div");
|
||||
body.className = "print-page-content";
|
||||
body.innerHTML = page.content || "";
|
||||
article.append(heading, body);
|
||||
hydrateAssets(article, { eager: true });
|
||||
printable.append(article);
|
||||
});
|
||||
|
||||
document.body.append(printable);
|
||||
document.body.classList.add("docslib-print-all");
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => window.print()));
|
||||
document.body.append(printable);
|
||||
await waitForPrintAssets(printable);
|
||||
document.body.classList.add("docslib-print-all");
|
||||
window.print();
|
||||
} catch (error) {
|
||||
removeFullPrintDocument();
|
||||
throw error;
|
||||
} finally {
|
||||
overlay.remove();
|
||||
}
|
||||
}
|
||||
|
||||
globalThis.PrintFullDocumentation = PrintFullDocumentation;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ def test_all_endpoints_and_self_contained_build(tmp_path):
|
|||
assert html_response.status_code == 200
|
||||
assert "http://127.0.0.1:9000/manifest.json" in html
|
||||
assert "http://127.0.0.1:9000/version" in html
|
||||
assert 'globalThis.COMPILED_FROM_VERSION = "0.3.8";' in html
|
||||
assert 'globalThis.COMPILED_FROM_VERSION = "0.3.9";' in html
|
||||
assert ".content { --custom-test: yes; }" in html
|
||||
assert r'document.body.dataset.customTest = \"yes\";' in html
|
||||
assert 'id="docslib-loading"' in html
|
||||
|
|
@ -160,10 +160,20 @@ def test_all_endpoints_and_self_contained_build(tmp_path):
|
|||
assert 'printable.id = "docslib-full-print"' in system_js
|
||||
assert "manifest.pages.forEach((page) =>" in system_js
|
||||
assert 'window.addEventListener("afterprint", removeFullPrintDocument)' in system_js
|
||||
assert "async function PrintFullDocumentation()" in system_js
|
||||
assert "await waitForPrintAssets(printable)" in system_js
|
||||
assert 'if (eager) element.loading = "eager"' in system_js
|
||||
assert "hydrateAssets(article, { eager: true })" in system_js
|
||||
assert 'image.addEventListener("load", resolve, { once: true })' in system_js
|
||||
assert 'await image.decode().catch(() => {})' in system_js
|
||||
assert "await document.fonts.ready" in system_js
|
||||
system_css = manifest["client"]["css"]["system"]
|
||||
assert 'body.docslib-print-all > .full-print-document { display: block; }' in system_css
|
||||
assert 'color: #000 !important; background: #fff !important;' in system_css
|
||||
assert ".print-page { break-after: page; page-break-after: always; }" in system_css
|
||||
assert ".print-loading-overlay { position: fixed; inset: 0; z-index: 2147483647; display: grid; width: 100vw; height: 100dvh" in system_css
|
||||
assert ".print-page-heading h1 { margin: 0; color: #000 !important; font-size: 1.35rem !important" in system_css
|
||||
assert ".print-page-content h1 { font-size: 1.8rem !important" in system_css
|
||||
assert "--shell: #000000" in html
|
||||
assert 'id="desktop-search-input"' in html
|
||||
assert '<header class="desktop-header">' in html
|
||||
|
|
@ -208,7 +218,7 @@ def test_all_endpoints_and_self_contained_build(tmp_path):
|
|||
|
||||
|
||||
def test_build_writes_static_bundle(tmp_path):
|
||||
assert __version__ == "0.3.8"
|
||||
assert __version__ == "0.3.9"
|
||||
|
||||
@Page("1", "Static page")
|
||||
def static_page():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue