166 lines
5.6 KiB
Plaintext
166 lines
5.6 KiB
Plaintext
---
|
|
// Digital Garden Graph Visualization
|
|
// This component creates a network graph visualization of content relationships
|
|
---
|
|
|
|
<div class="digital-garden-graph">
|
|
<div class="graph-container">
|
|
<svg id="digital-garden-svg" width="100%" height="400"></svg>
|
|
</div>
|
|
<div class="graph-legend">
|
|
<span class="legend-item">
|
|
<span class="dot post-dot"></span>
|
|
<span>Blog Posts</span>
|
|
</span>
|
|
<span class="legend-item">
|
|
<span class="dot config-dot"></span>
|
|
<span>Configurations</span>
|
|
</span>
|
|
<span class="legend-item">
|
|
<span class="dot project-dot"></span>
|
|
<span>Projects</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.digital-garden-graph {
|
|
margin: 2rem 0;
|
|
border-radius: 1rem;
|
|
border: 1px solid var(--card-border);
|
|
background-color: var(--card-bg);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.graph-container {
|
|
width: 100%;
|
|
height: 400px;
|
|
position: relative;
|
|
}
|
|
|
|
.graph-legend {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 2rem;
|
|
padding: 1rem;
|
|
border-top: 1px solid var(--card-border);
|
|
}
|
|
|
|
.legend-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: var(--text-secondary);
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.dot {
|
|
display: inline-block;
|
|
width: 12px;
|
|
height: 12px;
|
|
border-radius: 50%;
|
|
}
|
|
|
|
.post-dot {
|
|
background-color: var(--accent-primary);
|
|
}
|
|
|
|
.config-dot {
|
|
background-color: var(--accent-secondary);
|
|
}
|
|
|
|
.project-dot {
|
|
background-color: var(--accent-tertiary);
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const svg = document.getElementById('digital-garden-svg');
|
|
if (!svg) return;
|
|
|
|
// Set up the SVG dimensions
|
|
const width = svg.clientWidth;
|
|
const height = 400;
|
|
|
|
// Define nodes - this would typically come from your content metadata
|
|
// For this demo, we'll create a sample dataset
|
|
const nodes = [
|
|
{ id: 'k3s-cluster', type: 'post', x: width * 0.2, y: height * 0.3 },
|
|
{ id: 'prometheus', type: 'post', x: width * 0.4, y: height * 0.5 },
|
|
{ id: 'cloudflare-tunnel', type: 'post', x: width * 0.6, y: height * 0.3 },
|
|
{ id: 'gitops-flux', type: 'post', x: width * 0.5, y: height * 0.7 },
|
|
{ id: 'longhorn-storage', type: 'config', x: width * 0.3, y: height * 0.6 },
|
|
{ id: 'metallb', type: 'config', x: width * 0.7, y: height * 0.5 },
|
|
{ id: 'argobox', type: 'project', x: width * 0.5, y: height * 0.2 },
|
|
];
|
|
|
|
// Define links between nodes
|
|
const links = [
|
|
{ source: 'k3s-cluster', target: 'prometheus' },
|
|
{ source: 'k3s-cluster', target: 'longhorn-storage' },
|
|
{ source: 'k3s-cluster', target: 'argobox' },
|
|
{ source: 'prometheus', target: 'gitops-flux' },
|
|
{ source: 'cloudflare-tunnel', target: 'argobox' },
|
|
{ source: 'longhorn-storage', target: 'prometheus' },
|
|
{ source: 'argobox', target: 'metallb' },
|
|
{ source: 'metallb', target: 'cloudflare-tunnel' },
|
|
];
|
|
|
|
// Create SVG elements
|
|
// First the links
|
|
links.forEach(link => {
|
|
const sourceNode = nodes.find(n => n.id === link.source);
|
|
const targetNode = nodes.find(n => n.id === link.target);
|
|
|
|
if (sourceNode && targetNode) {
|
|
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
|
|
line.setAttribute('x1', sourceNode.x);
|
|
line.setAttribute('y1', sourceNode.y);
|
|
line.setAttribute('x2', targetNode.x);
|
|
line.setAttribute('y2', targetNode.y);
|
|
line.setAttribute('stroke', 'rgba(226, 232, 240, 0.1)');
|
|
line.setAttribute('stroke-width', '2');
|
|
svg.appendChild(line);
|
|
}
|
|
});
|
|
|
|
// Then the nodes
|
|
nodes.forEach(node => {
|
|
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
circle.setAttribute('cx', node.x);
|
|
circle.setAttribute('cy', node.y);
|
|
circle.setAttribute('r', '8');
|
|
|
|
// Assign color based on node type
|
|
if (node.type === 'post') {
|
|
circle.setAttribute('fill', 'var(--accent-primary)');
|
|
} else if (node.type === 'config') {
|
|
circle.setAttribute('fill', 'var(--accent-secondary)');
|
|
} else {
|
|
circle.setAttribute('fill', 'var(--accent-tertiary)');
|
|
}
|
|
|
|
// Add hover effect
|
|
circle.onmouseover = () => {
|
|
circle.setAttribute('r', '12');
|
|
circle.style.cursor = 'pointer';
|
|
};
|
|
circle.onmouseout = () => {
|
|
circle.setAttribute('r', '8');
|
|
};
|
|
|
|
// Add node label
|
|
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
text.setAttribute('x', node.x);
|
|
text.setAttribute('y', node.y + 20);
|
|
text.setAttribute('font-size', '12');
|
|
text.setAttribute('fill', 'var(--text-secondary)');
|
|
text.setAttribute('text-anchor', 'middle');
|
|
text.textContent = node.id.replace(/-/g, ' ');
|
|
|
|
svg.appendChild(circle);
|
|
svg.appendChild(text);
|
|
});
|
|
});
|
|
</script> |