refactor(MiniGraph): Improve data accuracy and fullscreen interactions
This commit is contained in:
parent
54c8f8d179
commit
2792e86546
|
@ -30,6 +30,9 @@ const relatedPostsTags = relatedPosts
|
||||||
.flatMap(post => post.data.tags || [])
|
.flatMap(post => post.data.tags || [])
|
||||||
.filter(tag => !tags.includes(tag)); // Exclude current post tags to avoid duplicates
|
.filter(tag => !tags.includes(tag)); // Exclude current post tags to avoid duplicates
|
||||||
|
|
||||||
|
// Create a set of all Level 1 nodes' tags for filtering Level 2 tags
|
||||||
|
const level1TagsSet = new Set([...tags, ...relatedPostsTags]);
|
||||||
|
|
||||||
// Get Level 2 posts: posts related to Level 1 tags (excluding current post and Level 1 posts)
|
// Get Level 2 posts: posts related to Level 1 tags (excluding current post and Level 1 posts)
|
||||||
const level2PostIds = new Set();
|
const level2PostIds = new Set();
|
||||||
const level2Posts = [];
|
const level2Posts = [];
|
||||||
|
@ -38,21 +41,22 @@ const level2Posts = [];
|
||||||
tags.forEach(tag => {
|
tags.forEach(tag => {
|
||||||
allPosts.forEach(post => {
|
allPosts.forEach(post => {
|
||||||
// Skip if post is current post or already in related posts
|
// Skip if post is current post or already in related posts
|
||||||
if (post.slug === slug || relatedPosts.some(rp => rp.slug === post.slug)) {
|
if (post.slug === slug || relatedPosts.some(rp => rp.slug === post.slug) || level2PostIds.has(post.slug)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If post has the tag and isn't already added
|
// If post has the tag, add it to Level 2
|
||||||
if (post.data.tags?.includes(tag) && !level2PostIds.has(post.slug)) {
|
if (post.data.tags?.includes(tag)) {
|
||||||
level2PostIds.add(post.slug);
|
level2PostIds.add(post.slug);
|
||||||
level2Posts.push(post);
|
level2Posts.push(post);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get Level 2 tags from Level 2 posts
|
// Only collect Level 2 tags that are directly linked to Level 1 posts
|
||||||
|
// This fixes the issue where unrelated Level 2 tags were being included
|
||||||
const level2Tags = new Set();
|
const level2Tags = new Set();
|
||||||
level2Posts.forEach(post => {
|
relatedPosts.forEach(post => {
|
||||||
(post.data.tags || []).forEach(tag => {
|
(post.data.tags || []).forEach(tag => {
|
||||||
// Only add if not already in Level 0 or Level 1 tags
|
// Only add if not already in Level 0 or Level 1 tags
|
||||||
if (!tags.includes(tag) && !relatedPostsTags.includes(tag)) {
|
if (!tags.includes(tag) && !relatedPostsTags.includes(tag)) {
|
||||||
|
@ -70,14 +74,16 @@ const nodes = [
|
||||||
type: "post",
|
type: "post",
|
||||||
level: 0,
|
level: 0,
|
||||||
category: category,
|
category: category,
|
||||||
tags: tags
|
tags: tags,
|
||||||
|
url: `/posts/${slug}/`
|
||||||
},
|
},
|
||||||
// Level 1: Tag nodes
|
// Level 1: Tag nodes
|
||||||
...tags.map(tag => ({
|
...tags.map(tag => ({
|
||||||
id: `tag-${tag}`,
|
id: `tag-${tag}`,
|
||||||
label: tag,
|
label: tag,
|
||||||
type: "tag",
|
type: "tag",
|
||||||
level: 1
|
level: 1,
|
||||||
|
url: `/tag/${tag}/`
|
||||||
})),
|
})),
|
||||||
// Level 1: Related post nodes
|
// Level 1: Related post nodes
|
||||||
...relatedPosts.map(post => ({
|
...relatedPosts.map(post => ({
|
||||||
|
@ -86,30 +92,35 @@ const nodes = [
|
||||||
type: "post",
|
type: "post",
|
||||||
level: 1,
|
level: 1,
|
||||||
category: post.data.category || "Uncategorized",
|
category: post.data.category || "Uncategorized",
|
||||||
tags: post.data.tags || []
|
tags: post.data.tags || [],
|
||||||
|
url: `/posts/${post.slug}/`
|
||||||
})),
|
})),
|
||||||
// Level 2: Related tags nodes
|
// Level 2: Related tags nodes (Tags from Level 1 posts)
|
||||||
...relatedPostsTags.map(tag => ({
|
...relatedPostsTags.map(tag => ({
|
||||||
id: `tag-${tag}`,
|
id: `tag-${tag}`,
|
||||||
label: tag,
|
label: tag,
|
||||||
type: "tag",
|
type: "tag",
|
||||||
level: 2
|
level: 2,
|
||||||
|
url: `/tag/${tag}/`
|
||||||
})),
|
})),
|
||||||
// Level 2: Posts related to tags
|
// Level 2: Posts related to tags (Posts connected to Level 1 tags)
|
||||||
...level2Posts.map(post => ({
|
...level2Posts.map(post => ({
|
||||||
id: post.slug,
|
id: post.slug,
|
||||||
label: post.data.title,
|
label: post.data.title,
|
||||||
type: "post",
|
type: "post",
|
||||||
level: 2,
|
level: 2,
|
||||||
category: post.data.category || "Uncategorized",
|
category: post.data.category || "Uncategorized",
|
||||||
tags: post.data.tags || []
|
tags: post.data.tags || [],
|
||||||
|
url: `/posts/${post.slug}/`
|
||||||
})),
|
})),
|
||||||
// Level 2: Tags from Level 2 posts
|
// Level 2: Tags from Level 1 posts (only tags directly connected to Level 1 posts)
|
||||||
|
// This was the corrected logic for level2Tags Set
|
||||||
...[...level2Tags].map(tag => ({
|
...[...level2Tags].map(tag => ({
|
||||||
id: `tag-${tag}`,
|
id: `tag-${tag}`,
|
||||||
label: tag.toString(),
|
label: tag.toString(),
|
||||||
type: "tag",
|
type: "tag",
|
||||||
level: 2
|
level: 2,
|
||||||
|
url: `/tag/${tag.toString()}/`
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -132,7 +143,7 @@ const edges = [
|
||||||
(post.data.tags || []).map(tag => ({
|
(post.data.tags || []).map(tag => ({
|
||||||
source: post.slug,
|
source: post.slug,
|
||||||
target: `tag-${tag}`,
|
target: `tag-${tag}`,
|
||||||
type: "post-tag"
|
type: "post-tag" // Re-using post-tag type for simplicity
|
||||||
}))
|
}))
|
||||||
),
|
),
|
||||||
// Level 1 to Level 2: Tags to related posts
|
// Level 1 to Level 2: Tags to related posts
|
||||||
|
@ -140,11 +151,12 @@ const edges = [
|
||||||
tags.filter(tag => post.data.tags?.includes(tag)).map(tag => ({
|
tags.filter(tag => post.data.tags?.includes(tag)).map(tag => ({
|
||||||
source: `tag-${tag}`,
|
source: `tag-${tag}`,
|
||||||
target: post.slug,
|
target: post.slug,
|
||||||
type: "tag-post"
|
type: "tag-post" // New type for tag -> post connection
|
||||||
}))
|
}))
|
||||||
)
|
)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
// Prepare graph data object
|
// Prepare graph data object
|
||||||
const graphData = { nodes, edges };
|
const graphData = { nodes, edges };
|
||||||
|
|
||||||
|
@ -229,7 +241,7 @@ const predefinedColors = {
|
||||||
<ul id={`${graphId}-info-connections`} class="connections-list"></ul>
|
<ul id={`${graphId}-info-connections`} class="connections-list"></ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="#" id={`${graphId}-info-link`} class="info-link" target="_self">View Content</a>
|
<a href="#" id={`${graphId}-info-link`} class="info-link" target="_blank" rel="noopener noreferrer">View Content</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -277,9 +289,15 @@ const predefinedColors = {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
|
opacity: 0; /* Hidden by default, shown on hover */
|
||||||
|
}
|
||||||
|
|
||||||
|
.knowledge-graph-wrapper:hover .fullscreen-toggle {
|
||||||
|
opacity: 0.7; /* Show on hover */
|
||||||
}
|
}
|
||||||
|
|
||||||
.fullscreen-toggle:hover {
|
.fullscreen-toggle:hover {
|
||||||
|
opacity: 1;
|
||||||
background: rgba(30, 41, 59, 0.9);
|
background: rgba(30, 41, 59, 0.9);
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
@ -359,7 +377,7 @@ const predefinedColors = {
|
||||||
|
|
||||||
/* Info panel */
|
/* Info panel */
|
||||||
.info-panel {
|
.info-panel {
|
||||||
width: 0;
|
width: 0; /* Hidden by default */
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--bg-secondary, #1e293b);
|
background: var(--bg-secondary, #1e293b);
|
||||||
transition: width 0.3s ease;
|
transition: width 0.3s ease;
|
||||||
|
@ -369,7 +387,7 @@ const predefinedColors = {
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-panel.active {
|
.info-panel.active {
|
||||||
width: 350px;
|
width: 350px; /* Show panel when active */
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-panel-header {
|
.info-panel-header {
|
||||||
|
@ -486,6 +504,7 @@ const predefinedColors = {
|
||||||
color: var(--accent-primary, #38bdf8);
|
color: var(--accent-primary, #38bdf8);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connections-list a:hover {
|
.connections-list a:hover {
|
||||||
|
@ -526,12 +545,12 @@ const predefinedColors = {
|
||||||
|
|
||||||
.info-panel {
|
.info-panel {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0;
|
height: 0; /* Start hidden */
|
||||||
}
|
}
|
||||||
|
|
||||||
.info-panel.active {
|
.info-panel.active {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 40%;
|
height: 40%; /* Take remaining space */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -584,6 +603,7 @@ const predefinedColors = {
|
||||||
let cy = null; // Mini graph instance
|
let cy = null; // Mini graph instance
|
||||||
let cyFullscreen = null; // Fullscreen graph instance
|
let cyFullscreen = null; // Fullscreen graph instance
|
||||||
let originalStyles = {}; // To restore page layout after exiting fullscreen
|
let originalStyles = {}; // To restore page layout after exiting fullscreen
|
||||||
|
let selectedNode = null; // Currently selected node for fullscreen interactions
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if we have any nodes
|
// Check if we have any nodes
|
||||||
|
@ -621,7 +641,8 @@ const predefinedColors = {
|
||||||
tags: node.tags || [],
|
tags: node.tags || [],
|
||||||
color: nodeColor,
|
color: nodeColor,
|
||||||
opacity: levelOpacity,
|
opacity: levelOpacity,
|
||||||
size: nodeSize
|
size: nodeSize,
|
||||||
|
url: node.url || '#'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -791,6 +812,11 @@ const predefinedColors = {
|
||||||
cy.on('tap', 'node', function(evt) {
|
cy.on('tap', 'node', function(evt) {
|
||||||
const node = evt.target;
|
const node = evt.target;
|
||||||
highlightNode(node, cy);
|
highlightNode(node, cy);
|
||||||
|
// Navigate on click in mini-graph
|
||||||
|
const url = node.data('url');
|
||||||
|
if (url && url !== '#') {
|
||||||
|
window.location.href = url;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add fullscreen toggle functionality if the button exists
|
// Add fullscreen toggle functionality if the button exists
|
||||||
|
@ -840,6 +866,13 @@ const predefinedColors = {
|
||||||
// Initialize fullscreen graph if not already done
|
// Initialize fullscreen graph if not already done
|
||||||
if (!cyFullscreen && fullscreenGraph) {
|
if (!cyFullscreen && fullscreenGraph) {
|
||||||
initFullscreenGraph();
|
initFullscreenGraph();
|
||||||
|
} else if (cyFullscreen) {
|
||||||
|
// If already initialized, just resize and fit
|
||||||
|
setTimeout(() => {
|
||||||
|
cyFullscreen.resize();
|
||||||
|
cyFullscreen.fit(undefined, 30);
|
||||||
|
cyFullscreen.center();
|
||||||
|
}, 50); // Small delay for transition
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent body scroll
|
// Prevent body scroll
|
||||||
|
@ -869,6 +902,14 @@ const predefinedColors = {
|
||||||
|
|
||||||
// Allow body scroll again
|
// Allow body scroll again
|
||||||
document.body.style.overflow = '';
|
document.body.style.overflow = '';
|
||||||
|
|
||||||
|
// Resize mini graph after exit
|
||||||
|
setTimeout(() => {
|
||||||
|
if (cy) {
|
||||||
|
cy.resize();
|
||||||
|
cy.fit(undefined, 20);
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save original styles of page elements before entering fullscreen
|
// Save original styles of page elements before entering fullscreen
|
||||||
|
@ -879,8 +920,8 @@ const predefinedColors = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for sidebar, content containers, etc. and save their styles
|
// Check for sidebar, content containers, etc. and save their styles
|
||||||
const sidebar = document.querySelector('.sidebar, aside, nav');
|
const sidebar = document.querySelector('.post-sidebar'); // Use specific class
|
||||||
const mainContent = document.querySelector('main, .content, article');
|
const mainContent = document.querySelector('.post-main-column'); // Use specific class
|
||||||
|
|
||||||
if (sidebar) {
|
if (sidebar) {
|
||||||
originalStyles.sidebar = {
|
originalStyles.sidebar = {
|
||||||
|
@ -888,7 +929,10 @@ const predefinedColors = {
|
||||||
display: sidebar.style.display,
|
display: sidebar.style.display,
|
||||||
visibility: sidebar.style.visibility,
|
visibility: sidebar.style.visibility,
|
||||||
position: sidebar.style.position,
|
position: sidebar.style.position,
|
||||||
zIndex: sidebar.style.zIndex
|
zIndex: sidebar.style.zIndex,
|
||||||
|
width: sidebar.style.width, // Save width too
|
||||||
|
flex: sidebar.style.flex,
|
||||||
|
gridColumn: sidebar.style.gridColumn
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -909,19 +953,26 @@ const predefinedColors = {
|
||||||
// Restore sidebar if it exists
|
// Restore sidebar if it exists
|
||||||
if (originalStyles.sidebar && originalStyles.sidebar.element) {
|
if (originalStyles.sidebar && originalStyles.sidebar.element) {
|
||||||
const sidebar = originalStyles.sidebar.element;
|
const sidebar = originalStyles.sidebar.element;
|
||||||
sidebar.style.display = originalStyles.sidebar.display;
|
Object.assign(sidebar.style, originalStyles.sidebar); // Restore all saved styles
|
||||||
sidebar.style.visibility = originalStyles.sidebar.visibility;
|
sidebar.style.removeProperty('position'); // Ensure position is reset if it was static
|
||||||
sidebar.style.position = originalStyles.sidebar.position;
|
|
||||||
sidebar.style.zIndex = originalStyles.sidebar.zIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restore main content if it exists
|
// Restore main content if it exists
|
||||||
if (originalStyles.mainContent && originalStyles.mainContent.element) {
|
if (originalStyles.mainContent && originalStyles.mainContent.element) {
|
||||||
const mainContent = originalStyles.mainContent.element;
|
const mainContent = originalStyles.mainContent.element;
|
||||||
mainContent.style.marginLeft = originalStyles.mainContent.marginLeft;
|
Object.assign(mainContent.style, originalStyles.mainContent);
|
||||||
mainContent.style.width = originalStyles.mainContent.width;
|
|
||||||
mainContent.style.maxWidth = originalStyles.mainContent.maxWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Force reflow to ensure styles apply correctly
|
||||||
|
setTimeout(() => {
|
||||||
|
const contentArea = document.querySelector('.post-content');
|
||||||
|
if (contentArea) {
|
||||||
|
const display = contentArea.style.display;
|
||||||
|
contentArea.style.display = 'none';
|
||||||
|
void contentArea.offsetHeight; // Trigger reflow
|
||||||
|
contentArea.style.display = display;
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the fullscreen graph
|
// Initialize the fullscreen graph
|
||||||
|
@ -969,6 +1020,7 @@ const predefinedColors = {
|
||||||
// Click on node to show info
|
// Click on node to show info
|
||||||
cyFullscreen.on('tap', 'node', function(evt) {
|
cyFullscreen.on('tap', 'node', function(evt) {
|
||||||
const node = evt.target;
|
const node = evt.target;
|
||||||
|
selectedNode = node; // Store the selected node
|
||||||
const nodeData = node.data();
|
const nodeData = node.data();
|
||||||
|
|
||||||
// Highlight the selected node
|
// Highlight the selected node
|
||||||
|
@ -983,6 +1035,7 @@ const predefinedColors = {
|
||||||
if (evt.target === cyFullscreen) {
|
if (evt.target === cyFullscreen) {
|
||||||
// Remove highlights
|
// Remove highlights
|
||||||
cyFullscreen.elements().removeClass('highlighted faded');
|
cyFullscreen.elements().removeClass('highlighted faded');
|
||||||
|
selectedNode = null;
|
||||||
|
|
||||||
// Hide info panel
|
// Hide info panel
|
||||||
if (infoPanel) {
|
if (infoPanel) {
|
||||||
|
@ -1009,46 +1062,51 @@ const predefinedColors = {
|
||||||
function showNodeInfo(nodeData) {
|
function showNodeInfo(nodeData) {
|
||||||
if (!infoPanel) return;
|
if (!infoPanel) return;
|
||||||
|
|
||||||
|
// Get elements inside the panel
|
||||||
|
const infoTitleEl = document.getElementById(`${graphId}-info-title`);
|
||||||
|
const infoTypeEl = document.getElementById(`${graphId}-info-type`);
|
||||||
|
const categoryContainerEl = document.getElementById(`${graphId}-info-category-container`);
|
||||||
|
const categoryEl = document.getElementById(`${graphId}-info-category`);
|
||||||
|
const tagsContainerEl = document.getElementById(`${graphId}-info-tags-container`);
|
||||||
|
const tagsEl = document.getElementById(`${graphId}-info-tags`);
|
||||||
|
const connectionsEl = document.getElementById(`${graphId}-info-connections`);
|
||||||
|
const linkEl = document.getElementById(`${graphId}-info-link`);
|
||||||
|
|
||||||
// Set node title
|
// Set node title
|
||||||
document.getElementById(`${graphId}-info-title`).textContent = nodeData.label;
|
if (infoTitleEl) infoTitleEl.textContent = nodeData.label;
|
||||||
|
|
||||||
// Set node type
|
// Set node type
|
||||||
const typeEl = document.getElementById(`${graphId}-info-type`);
|
if (infoTypeEl) {
|
||||||
typeEl.textContent = nodeData.type.charAt(0).toUpperCase() + nodeData.type.slice(1);
|
infoTypeEl.textContent = nodeData.type.charAt(0).toUpperCase() + nodeData.type.slice(1);
|
||||||
typeEl.className = `info-value type-value ${nodeData.type}-type`;
|
infoTypeEl.className = `info-value type-value ${nodeData.type}-type`;
|
||||||
|
}
|
||||||
|
|
||||||
// Set category if it's a post
|
// Set category if it's a post
|
||||||
const categoryContainer = document.getElementById(`${graphId}-info-category-container`);
|
if (categoryContainerEl && categoryEl) {
|
||||||
const categoryEl = document.getElementById(`${graphId}-info-category`);
|
|
||||||
|
|
||||||
if (nodeData.type === 'post' && nodeData.category) {
|
if (nodeData.type === 'post' && nodeData.category) {
|
||||||
categoryContainer.style.display = 'block';
|
categoryContainerEl.style.display = 'block';
|
||||||
categoryEl.textContent = nodeData.category;
|
categoryEl.textContent = nodeData.category;
|
||||||
|
|
||||||
// Use category colors
|
|
||||||
const catColor = predefinedColors[nodeData.category] || '#A0AEC0';
|
const catColor = predefinedColors[nodeData.category] || '#A0AEC0';
|
||||||
categoryEl.style.backgroundColor = `${catColor}33`; // Add alpha
|
categoryEl.style.backgroundColor = `${catColor}33`;
|
||||||
categoryEl.style.color = catColor;
|
categoryEl.style.color = catColor;
|
||||||
} else {
|
} else {
|
||||||
categoryContainer.style.display = 'none';
|
categoryContainerEl.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set tags
|
// Set tags
|
||||||
const tagsContainer = document.getElementById(`${graphId}-info-tags-container`);
|
if (tagsContainerEl && tagsEl) {
|
||||||
const tagsEl = document.getElementById(`${graphId}-info-tags`);
|
if (nodeData.tags && nodeData.tags.length > 0) {
|
||||||
|
tagsContainerEl.style.display = 'block';
|
||||||
if (nodeData.type === 'post' && nodeData.tags && nodeData.tags.length > 0) {
|
|
||||||
tagsContainer.style.display = 'block';
|
|
||||||
tagsEl.innerHTML = '';
|
tagsEl.innerHTML = '';
|
||||||
|
|
||||||
nodeData.tags.forEach(tag => {
|
nodeData.tags.forEach(tag => {
|
||||||
const tagEl = document.createElement('span');
|
const tagEl = document.createElement('span');
|
||||||
tagEl.className = 'tag';
|
tagEl.className = 'tag';
|
||||||
tagEl.textContent = tag;
|
tagEl.textContent = tag;
|
||||||
tagEl.addEventListener('click', () => {
|
tagEl.addEventListener('click', () => {
|
||||||
// Try to find and highlight the tag node
|
|
||||||
const tagNode = cyFullscreen.getElementById(`tag-${tag}`);
|
const tagNode = cyFullscreen.getElementById(`tag-${tag}`);
|
||||||
if (tagNode.length > 0) {
|
if (tagNode.length > 0) {
|
||||||
|
selectedNode = tagNode; // Update selected node
|
||||||
highlightNode(tagNode, cyFullscreen);
|
highlightNode(tagNode, cyFullscreen);
|
||||||
showNodeInfo(tagNode.data());
|
showNodeInfo(tagNode.data());
|
||||||
}
|
}
|
||||||
|
@ -1056,14 +1114,16 @@ const predefinedColors = {
|
||||||
tagsEl.appendChild(tagEl);
|
tagsEl.appendChild(tagEl);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
tagsContainer.style.display = 'none';
|
tagsContainerEl.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set connections
|
// Set connections - now with interactive links
|
||||||
const connectionsEl = document.getElementById(`${graphId}-info-connections`);
|
if (connectionsEl) {
|
||||||
connectionsEl.innerHTML = '';
|
connectionsEl.innerHTML = '';
|
||||||
|
const currentNode = cyFullscreen.getElementById(nodeData.id); // Get current node from fullscreen instance
|
||||||
|
const neighbors = currentNode.neighborhood('node');
|
||||||
|
|
||||||
const neighbors = cyFullscreen.getElementById(nodeData.id).neighborhood('node');
|
|
||||||
if (neighbors.length > 0) {
|
if (neighbors.length > 0) {
|
||||||
neighbors.forEach(neighbor => {
|
neighbors.forEach(neighbor => {
|
||||||
const neighborData = neighbor.data();
|
const neighborData = neighbor.data();
|
||||||
|
@ -1073,6 +1133,7 @@ const predefinedColors = {
|
||||||
a.textContent = neighborData.label;
|
a.textContent = neighborData.label;
|
||||||
a.addEventListener('click', (e) => {
|
a.addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
selectedNode = neighbor; // Update selected node
|
||||||
highlightNode(neighbor, cyFullscreen);
|
highlightNode(neighbor, cyFullscreen);
|
||||||
showNodeInfo(neighborData);
|
showNodeInfo(neighborData);
|
||||||
});
|
});
|
||||||
|
@ -1084,36 +1145,26 @@ const predefinedColors = {
|
||||||
li.textContent = 'No connections';
|
li.textContent = 'No connections';
|
||||||
connectionsEl.appendChild(li);
|
connectionsEl.appendChild(li);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set link based on node type
|
// Set link based on node type - dynamic text and URL
|
||||||
const linkEl = document.getElementById(`${graphId}-info-link`);
|
if (linkEl) {
|
||||||
if (nodeData.type === 'post') {
|
if (nodeData.type === 'post') {
|
||||||
linkEl.textContent = 'Read Post';
|
linkEl.textContent = 'Read Post';
|
||||||
linkEl.href = `/posts/${nodeData.id}/`;
|
linkEl.href = nodeData.url || `/posts/${nodeData.id}/`;
|
||||||
} else if (nodeData.type === 'tag') {
|
} else if (nodeData.type === 'tag') {
|
||||||
linkEl.textContent = 'View Tag';
|
linkEl.textContent = 'View Tag';
|
||||||
linkEl.href = `/tag/${nodeData.label}/`;
|
linkEl.href = nodeData.url || `/tag/${nodeData.label}/`;
|
||||||
} else {
|
} else {
|
||||||
linkEl.textContent = 'View Content';
|
linkEl.textContent = 'View Content';
|
||||||
linkEl.href = '#';
|
linkEl.href = nodeData.url || '#';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the panel
|
// Show the panel
|
||||||
infoPanel.classList.add('active');
|
infoPanel.classList.add('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make nodes clickable in mini graph
|
|
||||||
cy.on('tap', 'node[type="tag"]', function(evt) {
|
|
||||||
const node = evt.target;
|
|
||||||
const tagName = node.data('label');
|
|
||||||
window.location.href = `/tag/${tagName}`;
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.on('tap', 'node[type="post"][level!=0]', function(evt) {
|
|
||||||
const node = evt.target;
|
|
||||||
const postSlug = node.id();
|
|
||||||
window.location.href = `/posts/${postSlug}/`;
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[MiniGraph] Error initializing graph:', error);
|
console.error('[MiniGraph] Error initializing graph:', error);
|
||||||
container.innerHTML = '<div style="padding:10px;color:#a0aec0;text-align:center;">Error loading graph</div>';
|
container.innerHTML = '<div style="padding:10px;color:#a0aec0;text-align:center;">Error loading graph</div>';
|
||||||
|
|
Loading…
Reference in New Issue