task(G5): browser print stylesheet - @media print A4 landscape, screen-only chrome hidden, print-area fill, print service with cleanup

This commit is contained in:
Agent Zero
2026-08-29 02:52:12 +02:00
parent a093630d34
commit d9a0dffb05
4 changed files with 162 additions and 0 deletions
+11
View File
@@ -10,6 +10,7 @@ import React, { useCallback, useEffect, useState } from 'react';
import type { LayoutDefinition } from '../types/cad.types';
import { getActiveDocument, onActiveDocumentChanged } from '../kernel/document/documentService';
import { insertTitleBlockForLayout } from '../services/titleBlockService';
import { printLayout } from '../services/printService';
export interface LayoutView {
center: { x: number; y: number };
@@ -120,6 +121,10 @@ const LayoutBar: React.FC<LayoutBarProps> = ({ doc: docProp, bridge: bridgeProp
reload();
};
const handlePrint = () => {
void printLayout();
};
const handleZoomTo = (l: LayoutDefinition) => {
bridge?.zoomTo(l.viewport.center, l.viewport.scale);
};
@@ -137,6 +142,12 @@ const LayoutBar: React.FC<LayoutBarProps> = ({ doc: docProp, bridge: bridgeProp
onClick={handleAdd}
disabled={!bridge || !doc}
>+</button>
<button
type="button"
className="layout-bar-print screen-only"
title="Layout drucken (A4 landscape)"
onClick={handlePrint}
>🖨</button>
</div>
{layouts.length === 0 ? (
<div className="layout-bar-empty">Keine Layouts</div>
+37
View File
@@ -0,0 +1,37 @@
/**
* Task G5 Browser-Print-Service.
*
* Bereitet die App für window.print() vor: Der Body erhält die Klasse
* `print-layout`, das @media print Stylesheet blendet das UI-Chrome aus
* und zeigt nur den Druckbereich (Canvas/Layout). Nach dem Druck wird
* die Klasse wieder entfernt (kein DOM-Mutation-Restzustand).
*/
const PRINT_CLASS = 'print-layout';
/** Setzt den Print-Modus (Body-Klasse; @media print regelt das Rendering). */
export function preparePrintLayout(): void {
document.body.classList.add(PRINT_CLASS);
}
/** Entfernt den Print-Modus (nach Druck oder bei Abbruch). */
export function cleanupPrintLayout(): void {
document.body.classList.remove(PRINT_CLASS);
}
/**
* Druck-Flow: Vorbereitung → Rendern der nächsten Frames (Styles müssen
* greifen, bevor der Browser den Print-Dialog aufbaut) → window.print
* → Aufräumen. await-fähig für Tests (window.print wird gemockt).
*/
export async function printLayout(): Promise<void> {
preparePrintLayout();
// Zwei Frames warten: Style-Anwendung + Layout der Print-Ansicht
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
try {
window.print();
} finally {
cleanupPrintLayout();
}
}
+49
View File
@@ -2925,3 +2925,52 @@ body.drawer-open { overflow: hidden; }
border-radius: 4px;
}
.layout-bar-tb:hover { color: #4a90d9; background: rgba(74, 144, 217, 0.15); }
/* ═══════════════════════════════════════════════════════════
Task G5 Browser-Print: @media print zeigt nur den Druckbereich
═══════════════════════════════════════════════════════════ */
@media print {
@page {
size: A4 landscape;
margin: 10mm;
}
/* UI-Chrome im Druck ausblenden */
.screen-only {
display: none !important;
}
/* Druckbereich füllt die Seite */
.print-area {
width: 100% !important;
height: auto !important;
overflow: visible !important;
margin: 0 !important;
padding: 0 !important;
border: none !important;
}
/* Farben/Linien so drucken wie angezeigt */
.print-layout * {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
/* Body: kein Scroll, weiße Fläche */
body {
background: #fff !important;
overflow: hidden !important;
}
}
.layout-bar-print {
background: transparent;
color: #6b7280;
border: none;
cursor: pointer;
font-size: 13px;
padding: 2px 4px;
border-radius: 4px;
}
.layout-bar-print:hover { color: #4a90d9; background: rgba(74, 144, 217, 0.15); }