fix(rsocials): campaign planner loads with fit-to-view on initial visit

Previously the planner restored a stale zoomed-out viewport from
localStorage, and fitView() could fail silently if the SVG had zero
dimensions during shadow DOM layout. Now: skip viewport restore on
initial load, retry fitView up to 3 rAFs, and clamp min zoom to 50%.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2026-03-10 11:58:27 -07:00
parent 9a7548e5ca
commit 932151aa88
1 changed files with 10 additions and 5 deletions

View File

@ -193,6 +193,7 @@ class FolkCampaignPlanner extends HTMLElement {
private _boundPointerMove: ((e: PointerEvent) => void) | null = null;
private _boundPointerUp: ((e: PointerEvent) => void) | null = null;
private _boundKeyDown: ((e: KeyboardEvent) => void) | null = null;
private _initialLoad = true;
constructor() {
super();
@ -302,7 +303,7 @@ class FolkCampaignPlanner extends HTMLElement {
}
private restoreViewport() {
if (!this.currentFlowId) return;
if (!this.currentFlowId || this._initialLoad) return;
try {
const raw = localStorage.getItem(`rsocials:vp:${this.currentFlowId}`);
if (raw) {
@ -328,11 +329,14 @@ class FolkCampaignPlanner extends HTMLElement {
if (el) el.textContent = `${Math.round(this.canvasZoom * 100)}%`;
}
private fitView() {
private fitView(retries = 3) {
const svg = this.shadow.getElementById('cp-svg') as SVGSVGElement | null;
if (!svg || this.nodes.length === 0) return;
const rect = svg.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
if (rect.width === 0 || rect.height === 0) {
if (retries > 0) requestAnimationFrame(() => this.fitView(retries - 1));
return;
}
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (const n of this.nodes) {
@ -343,15 +347,16 @@ class FolkCampaignPlanner extends HTMLElement {
maxY = Math.max(maxY, n.position.y + s.h);
}
const pad = 60;
const pad = 40;
const contentW = maxX - minX + pad * 2;
const contentH = maxY - minY + pad * 2;
const scaleX = rect.width / contentW;
const scaleY = rect.height / contentH;
this.canvasZoom = Math.min(scaleX, scaleY, 1.5);
this.canvasZoom = Math.max(0.5, Math.min(scaleX, scaleY, 1.5));
this.canvasPanX = (rect.width - contentW * this.canvasZoom) / 2 - (minX - pad) * this.canvasZoom;
this.canvasPanY = (rect.height - contentH * this.canvasZoom) / 2 - (minY - pad) * this.canvasZoom;
this.updateCanvasTransform();
this._initialLoad = false;
}
private zoomAt(screenX: number, screenY: number, factor: number) {