fix(interactive): enable click handlers for SVG loaded via object tag
SUMMARY: Fixed interactive diagram click handlers not working. The SVG was loaded via <object> tag, which creates an isolated document that requires special access via contentDocument. ISSUE: - Clicks on service nodes had no effect - JavaScript was looking for SVG in main document - SVG loaded via <object> creates separate document context - document.getElementById() couldn't access elements inside object FIX: 1. Updated setup() to access object.contentDocument 2. Wait for object load event before initializing 3. Store SVG reference (this.svg) for later use 4. Updated all methods to use this.svg instead of document.getElementById() Methods updated: - setup(): Access SVG via objectElement.contentDocument - highlightService(): Use this.svg reference - unhighlightService(): Use this.svg reference - showServiceDetails(): Use this.svg reference - closePanel(): Use this.svg reference IMPACT: Interactive diagram now fully functional: ✓ Click any service node → detail panel appears ✓ Hover → connection lines highlight ✓ Close button → panel closes with animation ✓ Keyboard navigation works (Tab, Enter, Space) 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d63106f068
commit
3a091c02ba
2 changed files with 84 additions and 39 deletions
|
|
@ -4262,6 +4262,27 @@
|
|||
"file": "/home/theflow/projects/tractatus/public/index.html",
|
||||
"result": "passed",
|
||||
"reason": null
|
||||
},
|
||||
{
|
||||
"hook": "validate-file-edit",
|
||||
"timestamp": "2025-10-19T02:52:42.520Z",
|
||||
"file": "/home/theflow/projects/tractatus/public/js/components/interactive-diagram.js",
|
||||
"result": "passed",
|
||||
"reason": null
|
||||
},
|
||||
{
|
||||
"hook": "validate-file-edit",
|
||||
"timestamp": "2025-10-19T02:53:17.907Z",
|
||||
"file": "/home/theflow/projects/tractatus/public/js/components/interactive-diagram.js",
|
||||
"result": "passed",
|
||||
"reason": null
|
||||
},
|
||||
{
|
||||
"hook": "validate-file-edit",
|
||||
"timestamp": "2025-10-19T02:53:29.476Z",
|
||||
"file": "/home/theflow/projects/tractatus/public/js/components/interactive-diagram.js",
|
||||
"result": "passed",
|
||||
"reason": null
|
||||
}
|
||||
],
|
||||
"blocks": [
|
||||
|
|
@ -4489,9 +4510,9 @@
|
|||
}
|
||||
],
|
||||
"session_stats": {
|
||||
"total_edit_hooks": 424,
|
||||
"total_edit_hooks": 427,
|
||||
"total_edit_blocks": 32,
|
||||
"last_updated": "2025-10-19T02:50:18.922Z",
|
||||
"last_updated": "2025-10-19T02:53:29.476Z",
|
||||
"total_write_hooks": 185,
|
||||
"total_write_blocks": 5
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,62 +110,88 @@ class InteractiveDiagram {
|
|||
}
|
||||
|
||||
setup() {
|
||||
const svg = document.getElementById('interactive-arch-diagram');
|
||||
if (!svg) {
|
||||
console.warn('[InteractiveDiagram] SVG diagram not found');
|
||||
// SVG is loaded via <object> tag, need to access its contentDocument
|
||||
const objectElement = document.getElementById('interactive-svg-object');
|
||||
if (!objectElement) {
|
||||
console.warn('[InteractiveDiagram] SVG object element not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const nodes = svg.querySelectorAll('.service-node');
|
||||
console.log(`[InteractiveDiagram] Found ${nodes.length} service nodes`);
|
||||
// Wait for object to load
|
||||
const initializeSVG = () => {
|
||||
const svgDoc = objectElement.contentDocument;
|
||||
if (!svgDoc) {
|
||||
console.warn('[InteractiveDiagram] Could not access SVG contentDocument');
|
||||
return;
|
||||
}
|
||||
|
||||
nodes.forEach(node => {
|
||||
const serviceId = node.getAttribute('data-service');
|
||||
const svg = svgDoc.getElementById('interactive-arch-diagram');
|
||||
if (!svg) {
|
||||
console.warn('[InteractiveDiagram] SVG diagram not found in contentDocument');
|
||||
return;
|
||||
}
|
||||
|
||||
node.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.showServiceDetails(serviceId);
|
||||
// Store reference to SVG document for later use
|
||||
this.svgDoc = svgDoc;
|
||||
this.svg = svg;
|
||||
|
||||
const nodes = svg.querySelectorAll('.service-node');
|
||||
console.log(`[InteractiveDiagram] Found ${nodes.length} service nodes`);
|
||||
|
||||
nodes.forEach(node => {
|
||||
const serviceId = node.getAttribute('data-service');
|
||||
|
||||
node.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
this.showServiceDetails(serviceId);
|
||||
});
|
||||
|
||||
node.addEventListener('mouseenter', () => {
|
||||
this.highlightService(serviceId);
|
||||
});
|
||||
|
||||
node.addEventListener('mouseleave', () => {
|
||||
this.unhighlightService(serviceId);
|
||||
});
|
||||
});
|
||||
|
||||
node.addEventListener('mouseenter', () => {
|
||||
this.highlightService(serviceId);
|
||||
});
|
||||
this.addKeyboardNavigation(nodes);
|
||||
};
|
||||
|
||||
node.addEventListener('mouseleave', () => {
|
||||
this.unhighlightService(serviceId);
|
||||
});
|
||||
});
|
||||
|
||||
this.addKeyboardNavigation(nodes);
|
||||
// If object already loaded, initialize immediately
|
||||
if (objectElement.contentDocument) {
|
||||
initializeSVG();
|
||||
} else {
|
||||
// Otherwise wait for load event
|
||||
objectElement.addEventListener('load', initializeSVG);
|
||||
}
|
||||
}
|
||||
|
||||
highlightService(serviceId) {
|
||||
const svg = document.getElementById('interactive-arch-diagram');
|
||||
if (!svg) return;
|
||||
if (!this.svg) return;
|
||||
|
||||
const connectionLine = svg.querySelector(`#conn-${serviceId}`);
|
||||
const connectionLine = this.svg.querySelector(`#conn-${serviceId}`);
|
||||
if (connectionLine) {
|
||||
connectionLine.classList.add('active');
|
||||
}
|
||||
|
||||
const node = svg.querySelector(`#node-${serviceId}`);
|
||||
const node = this.svg.querySelector(`#node-${serviceId}`);
|
||||
if (node) {
|
||||
node.classList.add('hover');
|
||||
}
|
||||
}
|
||||
|
||||
unhighlightService(serviceId) {
|
||||
const svg = document.getElementById('interactive-arch-diagram');
|
||||
if (!svg) return;
|
||||
if (!this.svg) return;
|
||||
|
||||
if (this.activeService === serviceId) return;
|
||||
|
||||
const connectionLine = svg.querySelector(`#conn-${serviceId}`);
|
||||
const connectionLine = this.svg.querySelector(`#conn-${serviceId}`);
|
||||
if (connectionLine) {
|
||||
connectionLine.classList.remove('active');
|
||||
}
|
||||
|
||||
const node = svg.querySelector(`#node-${serviceId}`);
|
||||
const node = this.svg.querySelector(`#node-${serviceId}`);
|
||||
if (node) {
|
||||
node.classList.remove('hover');
|
||||
}
|
||||
|
|
@ -180,17 +206,16 @@ class InteractiveDiagram {
|
|||
|
||||
this.activeService = serviceId;
|
||||
|
||||
const svg = document.getElementById('interactive-arch-diagram');
|
||||
if (svg) {
|
||||
svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active'));
|
||||
svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active'));
|
||||
if (this.svg) {
|
||||
this.svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active'));
|
||||
this.svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active'));
|
||||
|
||||
const node = svg.querySelector(`#node-${serviceId}`);
|
||||
const node = this.svg.querySelector(`#node-${serviceId}`);
|
||||
if (node) {
|
||||
node.classList.add('active');
|
||||
}
|
||||
|
||||
const connectionLine = svg.querySelector(`#conn-${serviceId}`);
|
||||
const connectionLine = this.svg.querySelector(`#conn-${serviceId}`);
|
||||
if (connectionLine) {
|
||||
connectionLine.classList.add('active');
|
||||
}
|
||||
|
|
@ -318,10 +343,9 @@ class InteractiveDiagram {
|
|||
}, 300);
|
||||
}
|
||||
|
||||
const svg = document.getElementById('interactive-arch-diagram');
|
||||
if (svg) {
|
||||
svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active'));
|
||||
svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active'));
|
||||
if (this.svg) {
|
||||
this.svg.querySelectorAll('.service-node').forEach(n => n.classList.remove('active'));
|
||||
this.svg.querySelectorAll('.connection-line').forEach(l => l.classList.remove('active'));
|
||||
}
|
||||
|
||||
this.activeService = null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue