Erweiterungen
This commit is contained in:
@@ -41,6 +41,7 @@ import com.vaadin.flow.component.textfield.NumberField;
|
|||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Route(value = "pdf-builder", layout = MainLayout.class)
|
@Route(value = "pdf-builder", layout = MainLayout.class)
|
||||||
@PageTitle("PDF Builder")
|
@PageTitle("PDF Builder")
|
||||||
@@ -53,6 +54,9 @@ public class PDFBuilderView extends Div {
|
|||||||
private final PdfTemplateService templateService;
|
private final PdfTemplateService templateService;
|
||||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||||
private VerticalLayout inspectorList;
|
private VerticalLayout inspectorList;
|
||||||
|
// Serverseitige Fallback-Liste der Frames (zeigt Einträge selbst dann an,
|
||||||
|
// wenn das DOM-Lesen via JS temporär fehlschlägt)
|
||||||
|
private final List<FrameInfo> serverFrames = new ArrayList<>();
|
||||||
|
|
||||||
public PDFBuilderView(PDFGenerationService pdfService, PdfTemplateService templateService) {
|
public PDFBuilderView(PDFGenerationService pdfService, PdfTemplateService templateService) {
|
||||||
this.pdfService = pdfService;
|
this.pdfService = pdfService;
|
||||||
@@ -124,7 +128,7 @@ public class PDFBuilderView extends Div {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getCollectStateJs() {
|
private String getCollectStateJs() {
|
||||||
return "(function(){" +
|
return "return (function(){" +
|
||||||
" const root = document.getElementById('pdf-canvas');\n" +
|
" const root = document.getElementById('pdf-canvas');\n" +
|
||||||
" if(!root){ return JSON.stringify({items:[]}); }\n" +
|
" if(!root){ return JSON.stringify({items:[]}); }\n" +
|
||||||
" root.classList.add('capturing');\n" +
|
" root.classList.add('capturing');\n" +
|
||||||
@@ -173,21 +177,49 @@ public class PDFBuilderView extends Div {
|
|||||||
UI.getCurrent().getPage().executeJs(js).then(String.class, json -> {
|
UI.getCurrent().getPage().executeJs(js).then(String.class, json -> {
|
||||||
try {
|
try {
|
||||||
String safeJson = (json == null || json.isBlank()) ? "[]" : json;
|
String safeJson = (json == null || json.isBlank()) ? "[]" : json;
|
||||||
|
safeJson = safeJson.trim();
|
||||||
|
if ("null".equalsIgnoreCase(safeJson) || "undefined".equalsIgnoreCase(safeJson)) {
|
||||||
|
safeJson = "[]";
|
||||||
|
}
|
||||||
List<FrameInfo> frames = objectMapper.readValue(safeJson, new TypeReference<List<FrameInfo>>(){});
|
List<FrameInfo> frames = objectMapper.readValue(safeJson, new TypeReference<List<FrameInfo>>(){});
|
||||||
renderInspectorItems(frames);
|
// Erfolg: serverseitige Liste ersetzen und rendern
|
||||||
|
serverFrames.clear();
|
||||||
|
if (frames != null) { serverFrames.addAll(frames); }
|
||||||
|
renderInspectorItems(serverFrames);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Notification.show("Inspector-Update fehlgeschlagen: " + ex.getMessage(), 3000, Notification.Position.MIDDLE);
|
// Fallback: Letzten bekannten Stand aus serverFrames rendern
|
||||||
|
renderInspectorItems(serverFrames);
|
||||||
|
Notification.show("Inspector-Update fehlgeschlagen (Fallback genutzt): " + ex.getMessage(), 3000, Notification.Position.MIDDLE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshInspectorFromDomDeferred() {
|
||||||
|
UI ui = UI.getCurrent();
|
||||||
|
if (ui == null) {
|
||||||
|
refreshInspectorFromDom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Nach dem nächsten Paint (requestAnimationFrame) den Server anstoßen,
|
||||||
|
// damit der Inspector NACH den DOM-Updates liest.
|
||||||
|
ui.getPage().executeJs("requestAnimationFrame(() => { try { if ($0 && $0.$server && $0.$server.onAfterFrameMutation) { $0.$server.onAfterFrameMutation(); } } catch(e){} });", this.getElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
@com.vaadin.flow.component.ClientCallable
|
||||||
|
private void onAfterFrameMutation() {
|
||||||
|
refreshInspectorFromDom();
|
||||||
|
}
|
||||||
|
|
||||||
private void renderInspectorItems(List<FrameInfo> frames) {
|
private void renderInspectorItems(List<FrameInfo> frames) {
|
||||||
inspectorList.removeAll();
|
inspectorList.removeAll();
|
||||||
if (frames == null || frames.isEmpty()) return;
|
if (frames == null || frames.isEmpty()) return;
|
||||||
for (FrameInfo f : frames) {
|
for (FrameInfo f : frames) {
|
||||||
|
if (f == null) { continue; }
|
||||||
Div row = new Div();
|
Div row = new Div();
|
||||||
row.addClassName("inspector-item");
|
row.addClassName("inspector-item");
|
||||||
Span label = new Span(("text".equals(f.type) ? "Text" : "Bild") + " (" + f.id + ")");
|
String idText = (f.id == null ? "" : f.id);
|
||||||
|
String typeText = ("text".equals(f.type) ? "Text" : "Bild");
|
||||||
|
Span label = new Span(typeText + " (" + idText + ")");
|
||||||
NumberField x = makeField("X", f.x);
|
NumberField x = makeField("X", f.x);
|
||||||
NumberField y = makeField("Y", f.y);
|
NumberField y = makeField("Y", f.y);
|
||||||
NumberField w = makeField("Breite", f.width);
|
NumberField w = makeField("Breite", f.width);
|
||||||
@@ -219,15 +251,39 @@ public class PDFBuilderView extends Div {
|
|||||||
double dh = h == null ? 10 : Math.max(10, h);
|
double dh = h == null ? 10 : Math.max(10, h);
|
||||||
String js = "(function(){ const el = document.getElementById('" + id + "'); if(!el) return; el.style.left='" + (int)dx + "px'; el.style.top='" + (int)dy + "px'; el.style.width='" + (int)dw + "px'; el.style.height='" + (int)dh + "px'; })()";
|
String js = "(function(){ const el = document.getElementById('" + id + "'); if(!el) return; el.style.left='" + (int)dx + "px'; el.style.top='" + (int)dy + "px'; el.style.width='" + (int)dw + "px'; el.style.height='" + (int)dh + "px'; })()";
|
||||||
UI.getCurrent().getPage().executeJs(js);
|
UI.getCurrent().getPage().executeJs(js);
|
||||||
// Danach Inspector neu laden
|
// Serverliste aktualisieren
|
||||||
refreshInspectorFromDom();
|
upsertServerFrame(id, null, (int)dx, (int)dy, (int)dw, (int)dh);
|
||||||
|
// Danach Inspector neu laden (deferred, damit Styles/DOM sicher angewendet sind)
|
||||||
|
refreshInspectorFromDomDeferred();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upsertServerFrame(String id, String type, Integer x, Integer y, Integer width, Integer height) {
|
||||||
|
if (id == null || id.isBlank()) return;
|
||||||
|
FrameInfo found = null;
|
||||||
|
for (FrameInfo fi : serverFrames) { if (fi != null && id.equals(fi.id)) { found = fi; break; } }
|
||||||
|
if (found == null) {
|
||||||
|
found = new FrameInfo();
|
||||||
|
found.id = id;
|
||||||
|
serverFrames.add(found);
|
||||||
|
}
|
||||||
|
if (type != null) found.type = type;
|
||||||
|
if (x != null) found.x = x;
|
||||||
|
if (y != null) found.y = y;
|
||||||
|
if (width != null) found.width = width;
|
||||||
|
if (height != null) found.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getFramesInspectorJs(){
|
private String getFramesInspectorJs(){
|
||||||
return "(function(){\n" +
|
return "return (function(){\n" +
|
||||||
" const root = document.getElementById('pdf-canvas');\n" +
|
" const root = document.getElementById('pdf-canvas');\n" +
|
||||||
" if(!root){ return '[]'; }\n" +
|
" let frames = [];\n" +
|
||||||
" const items = Array.from(root.querySelectorAll('.canvas-frame')).map(f=>{\n" +
|
" if (root) {\n" +
|
||||||
|
" frames = Array.from(root.querySelectorAll('.canvas-frame'));\n" +
|
||||||
|
" } else {\n" +
|
||||||
|
" // Fallback: global Suche (falls #pdf-canvas z.B. durch Slot/Shadow nicht direkt gefunden wird)\n" +
|
||||||
|
" frames = Array.from(document.querySelectorAll('#pdf-canvas .canvas-frame, .pdf-canvas .canvas-frame, .canvas-frame'));\n" +
|
||||||
|
" }\n" +
|
||||||
|
" const items = frames.map(f=>{\n" +
|
||||||
" const cs = getComputedStyle(f);\n" +
|
" const cs = getComputedStyle(f);\n" +
|
||||||
" const id = f.id || f.getAttribute('data-frame-id') || '';\n" +
|
" const id = f.id || f.getAttribute('data-frame-id') || '';\n" +
|
||||||
" const type = f.classList.contains('text-frame') ? 'text' : 'image';\n" +
|
" const type = f.classList.contains('text-frame') ? 'text' : 'image';\n" +
|
||||||
@@ -362,6 +418,15 @@ public class PDFBuilderView extends Div {
|
|||||||
enableCanvasDeactivation(inner);
|
enableCanvasDeactivation(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void refreshInspectorAfterClientFlush() {
|
||||||
|
UI ui = UI.getCurrent();
|
||||||
|
if (ui == null) {
|
||||||
|
PDFBuilderView.this.refreshInspectorFromDom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui.beforeClientResponse(PdfCanvas.this, ctx -> PDFBuilderView.this.refreshInspectorFromDom());
|
||||||
|
}
|
||||||
|
|
||||||
private void enableDrop(Div target) {
|
private void enableDrop(Div target) {
|
||||||
// Dragover/Drops auf dem Canvas erfassen und an Server melden
|
// Dragover/Drops auf dem Canvas erfassen und an Server melden
|
||||||
target.getElement().executeJs(
|
target.getElement().executeJs(
|
||||||
@@ -410,7 +475,7 @@ public class PDFBuilderView extends Div {
|
|||||||
@com.vaadin.flow.component.ClientCallable
|
@com.vaadin.flow.component.ClientCallable
|
||||||
private void onFrameChanged() {
|
private void onFrameChanged() {
|
||||||
// Nach Drag/Resize: Inspector-Seite aktualisieren
|
// Nach Drag/Resize: Inspector-Seite aktualisieren
|
||||||
PDFBuilderView.this.refreshInspectorFromDom();
|
PDFBuilderView.this.refreshInspectorFromDomDeferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addTextFrame(int x, int y) {
|
private void addTextFrame(int x, int y) {
|
||||||
@@ -481,8 +546,11 @@ public class PDFBuilderView extends Div {
|
|||||||
enableFrameDragging(frame);
|
enableFrameDragging(frame);
|
||||||
// Aktivierungsverhalten
|
// Aktivierungsverhalten
|
||||||
enableFrameActivation(frame);
|
enableFrameActivation(frame);
|
||||||
// Inspector aktualisieren
|
// Serverliste sofort updaten und rendern, damit der Eintrag direkt erscheint
|
||||||
PDFBuilderView.this.refreshInspectorFromDom();
|
upsertServerFrame(id, "text", x, y, 300, 140);
|
||||||
|
renderInspectorItems(serverFrames);
|
||||||
|
// Zusätzlich clientseitig deferred refresher, um DOM-Werte zu synchronisieren
|
||||||
|
PDFBuilderView.this.refreshInspectorFromDomDeferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void promptImageUpload(int x, int y) {
|
private void promptImageUpload(int x, int y) {
|
||||||
@@ -542,8 +610,11 @@ public class PDFBuilderView extends Div {
|
|||||||
enableFrameDragging(frame);
|
enableFrameDragging(frame);
|
||||||
// Aktivierungsverhalten
|
// Aktivierungsverhalten
|
||||||
enableFrameActivation(frame);
|
enableFrameActivation(frame);
|
||||||
// Inspector aktualisieren
|
// Serverliste sofort updaten und rendern
|
||||||
PDFBuilderView.this.refreshInspectorFromDom();
|
upsertServerFrame(id, "image", x, y, 260, 180);
|
||||||
|
renderInspectorItems(serverFrames);
|
||||||
|
// Zusätzlich deferred refresher
|
||||||
|
PDFBuilderView.this.refreshInspectorFromDomDeferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableFrameDragging(Div frame) {
|
private void enableFrameDragging(Div frame) {
|
||||||
|
|||||||
Reference in New Issue
Block a user