fix: Transak modal not opening — namespace querySelector + wallet check
- Use DOM API (createElementNS + createElement) to build foreignObject panel instead of innerHTML, ensuring proper HTML namespace for querySelector to traverse SVG→HTML boundary - Query buttons/inputs from HTML panel root instead of SVG overlay - Remove wallet address requirement for demo mode (use zero address) - Add console logging and .catch() for Transak widget debugging Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1335c38d48
commit
f9bafc8ef0
|
|
@ -2491,15 +2491,21 @@ class FolkFlowsApp extends HTMLElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Source/outcome: keep config panel
|
// Source/outcome: keep config panel — use DOM APIs for proper namespace handling
|
||||||
const panelW = 280;
|
const panelW = 280;
|
||||||
const panelH = 260;
|
const panelH = 260;
|
||||||
const panelX = s.w + 12;
|
const panelX = s.w + 12;
|
||||||
const panelY = 0;
|
const panelY = 0;
|
||||||
|
|
||||||
overlay.innerHTML += `
|
const fo = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
|
||||||
<foreignObject x="${panelX}" y="${panelY}" width="${panelW}" height="${panelH}">
|
fo.setAttribute("x", String(panelX));
|
||||||
<div xmlns="http://www.w3.org/1999/xhtml" class="inline-config-panel" style="height:${panelH}px">
|
fo.setAttribute("y", String(panelY));
|
||||||
|
fo.setAttribute("width", String(panelW));
|
||||||
|
fo.setAttribute("height", String(panelH));
|
||||||
|
const panelDiv = document.createElement("div");
|
||||||
|
panelDiv.className = "inline-config-panel";
|
||||||
|
panelDiv.style.height = `${panelH}px`;
|
||||||
|
panelDiv.innerHTML = `
|
||||||
<div class="icp-tabs">
|
<div class="icp-tabs">
|
||||||
<button class="icp-tab icp-tab--active" data-icp-tab="config">Config</button>
|
<button class="icp-tab icp-tab--active" data-icp-tab="config">Config</button>
|
||||||
<button class="icp-tab" data-icp-tab="analytics">Analytics</button>
|
<button class="icp-tab" data-icp-tab="analytics">Analytics</button>
|
||||||
|
|
@ -2510,9 +2516,9 @@ class FolkFlowsApp extends HTMLElement {
|
||||||
<button class="iet-done" style="background:var(--rflows-btn-done);color:white">Done</button>
|
<button class="iet-done" style="background:var(--rflows-btn-done);color:white">Done</button>
|
||||||
<button class="iet-delete" style="background:var(--rflows-btn-delete);color:white">Delete</button>
|
<button class="iet-delete" style="background:var(--rflows-btn-delete);color:white">Delete</button>
|
||||||
<button class="iet-panel" style="background:var(--rs-border-strong);color:var(--rs-text-primary)">...</button>
|
<button class="iet-panel" style="background:var(--rs-border-strong);color:var(--rs-text-primary)">...</button>
|
||||||
</div>
|
</div>`;
|
||||||
</div>
|
fo.appendChild(panelDiv);
|
||||||
</foreignObject>`;
|
overlay.appendChild(fo);
|
||||||
|
|
||||||
g.appendChild(overlay);
|
g.appendChild(overlay);
|
||||||
this.attachInlineConfigListeners(g, node);
|
this.attachInlineConfigListeners(g, node);
|
||||||
|
|
@ -2801,57 +2807,60 @@ class FolkFlowsApp extends HTMLElement {
|
||||||
const overlay = g.querySelector(".inline-edit-overlay");
|
const overlay = g.querySelector(".inline-edit-overlay");
|
||||||
if (!overlay) return;
|
if (!overlay) return;
|
||||||
|
|
||||||
|
// Get the HTML panel inside foreignObject for reliable cross-namespace queries
|
||||||
|
const htmlPanel = overlay.querySelector("foreignObject")?.querySelector(".inline-config-panel") as HTMLElement | null;
|
||||||
|
const queryRoot = htmlPanel || overlay;
|
||||||
|
|
||||||
// Tab switching
|
// Tab switching
|
||||||
overlay.querySelectorAll(".icp-tab").forEach((el) => {
|
queryRoot.querySelectorAll(".icp-tab").forEach((el) => {
|
||||||
el.addEventListener("click", (e: Event) => {
|
el.addEventListener("click", (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const tab = (el as HTMLElement).dataset.icpTab as "config" | "analytics" | "allocations";
|
const tab = (el as HTMLElement).dataset.icpTab as "config" | "analytics" | "allocations";
|
||||||
if (!tab || tab === this.inlineConfigTab) return;
|
if (!tab || tab === this.inlineConfigTab) return;
|
||||||
this.inlineConfigTab = tab;
|
this.inlineConfigTab = tab;
|
||||||
overlay.querySelectorAll(".icp-tab").forEach((t) => t.classList.remove("icp-tab--active"));
|
queryRoot.querySelectorAll(".icp-tab").forEach((t) => t.classList.remove("icp-tab--active"));
|
||||||
el.classList.add("icp-tab--active");
|
el.classList.add("icp-tab--active");
|
||||||
const body = overlay.querySelector(".icp-body") as HTMLElement;
|
const body = queryRoot.querySelector(".icp-body") as HTMLElement;
|
||||||
if (body) body.innerHTML = this.renderInlineConfigContent(node);
|
if (body) body.innerHTML = this.renderInlineConfigContent(node);
|
||||||
this.attachInlineConfigFieldListeners(overlay as Element, node);
|
this.attachInlineConfigFieldListeners(queryRoot as Element, node);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Field listeners
|
// Field listeners
|
||||||
this.attachInlineConfigFieldListeners(overlay, node);
|
this.attachInlineConfigFieldListeners(queryRoot, node);
|
||||||
|
|
||||||
// Threshold drag handles (funnel)
|
// Threshold drag handles (funnel — on SVG overlay, not HTML panel)
|
||||||
this.attachThresholdDragListeners(overlay, node);
|
this.attachThresholdDragListeners(overlay, node);
|
||||||
|
|
||||||
// Done button
|
// Done button
|
||||||
overlay.querySelector(".iet-done")?.addEventListener("click", (e: Event) => {
|
queryRoot.querySelector(".iet-done")?.addEventListener("click", (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.exitInlineEdit();
|
this.exitInlineEdit();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Delete button
|
// Delete button
|
||||||
overlay.querySelector(".iet-delete")?.addEventListener("click", (e: Event) => {
|
queryRoot.querySelector(".iet-delete")?.addEventListener("click", (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.deleteNode(node.id);
|
this.deleteNode(node.id);
|
||||||
this.exitInlineEdit();
|
this.exitInlineEdit();
|
||||||
});
|
});
|
||||||
|
|
||||||
// "..." panel button
|
// "..." panel button
|
||||||
overlay.querySelector(".iet-panel")?.addEventListener("click", (e: Event) => {
|
queryRoot.querySelector(".iet-panel")?.addEventListener("click", (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.exitInlineEdit();
|
this.exitInlineEdit();
|
||||||
this.openEditor(node.id);
|
this.openEditor(node.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fund Now button (source card type)
|
// Fund Now button (source card type)
|
||||||
overlay.querySelector("[data-icp-action='fund']")?.addEventListener("click", (e: Event) => {
|
const fundBtn = queryRoot.querySelector("[data-icp-action='fund']");
|
||||||
|
fundBtn?.addEventListener("click", (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const sd = node.data as SourceNodeData;
|
const sd = node.data as SourceNodeData;
|
||||||
const flowId = this.flowId || this.getAttribute("flow-id") || "";
|
const flowId = this.flowId || this.getAttribute("flow-id") || "";
|
||||||
if (!sd.walletAddress) {
|
// Use configured wallet or demo default (Transak staging accepts any valid address)
|
||||||
alert("Configure a wallet address first");
|
const wallet = sd.walletAddress || "0x0000000000000000000000000000000000000000";
|
||||||
return;
|
this.openTransakWidget(flowId, wallet).catch((err) => console.error("[Transak] Error:", err));
|
||||||
}
|
|
||||||
this.openTransakWidget(flowId, sd.walletAddress);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Click-outside handler — listen on shadow root to avoid retargeting issues
|
// Click-outside handler — listen on shadow root to avoid retargeting issues
|
||||||
|
|
@ -3174,6 +3183,7 @@ class FolkFlowsApp extends HTMLElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async openTransakWidget(flowId: string, walletAddress: string) {
|
private async openTransakWidget(flowId: string, walletAddress: string) {
|
||||||
|
console.log("[Transak] Opening widget for flow:", flowId, "wallet:", walletAddress);
|
||||||
// Fetch Transak config from server
|
// Fetch Transak config from server
|
||||||
let apiKey = "STAGING_KEY";
|
let apiKey = "STAGING_KEY";
|
||||||
let env = "STAGING";
|
let env = "STAGING";
|
||||||
|
|
@ -3633,11 +3643,8 @@ class FolkFlowsApp extends HTMLElement {
|
||||||
// Fund with card
|
// Fund with card
|
||||||
backdrop.querySelector('[data-action="fund-with-card"]')?.addEventListener("click", () => {
|
backdrop.querySelector('[data-action="fund-with-card"]')?.addEventListener("click", () => {
|
||||||
const flowId = this.flowId || this.getAttribute("flow-id") || "";
|
const flowId = this.flowId || this.getAttribute("flow-id") || "";
|
||||||
if (!d.walletAddress) {
|
const wallet = d.walletAddress || "0x0000000000000000000000000000000000000000";
|
||||||
alert("Configure a wallet address first (use rIdentity passkey or enter manually)");
|
this.openTransakWidget(flowId, wallet);
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.openTransakWidget(flowId, d.walletAddress);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Connect with EncryptID
|
// Connect with EncryptID
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue