diff --git a/src/components/FrameRenderer/FrameRenderer.astro b/src/components/FrameRenderer/FrameRenderer.astro
index fb90eb446..86d4d3695 100644
--- a/src/components/FrameRenderer/FrameRenderer.astro
+++ b/src/components/FrameRenderer/FrameRenderer.astro
@@ -4,7 +4,8 @@ import TopicOverlay from '../TopicOverlay/TopicOverlay.astro';
import './FrameRenderer.css';
export interface Props {
- roadmapId: string;
+ resourceType: 'roadmap' | 'best-practice';
+ resourceId: string;
jsonUrl: string;
dimensions?: {
width: number;
@@ -12,13 +13,14 @@ export interface Props {
};
}
-const { roadmapId, jsonUrl, dimensions = null } = Astro.props;
+const { resourceId, resourceType, jsonUrl, dimensions = null } = Astro.props;
---
diff --git a/src/components/FrameRenderer/renderer.js b/src/components/FrameRenderer/renderer.js
index 55b09e544..ddb25f551 100644
--- a/src/components/FrameRenderer/renderer.js
+++ b/src/components/FrameRenderer/renderer.js
@@ -1,23 +1,17 @@
import { wireframeJSONToSVG } from 'roadmap-renderer';
-/**
- * @typedef {{ roadmapId: string, jsonUrl: string }} RoadmapConfig
- */
-
export class Renderer {
- /**
- * @param {RoadmapConfig} config
- */
constructor() {
- this.roadmapId = '';
+ this.resourceId = '';
+ this.resourceType = '';
this.jsonUrl = '';
- this.containerId = 'roadmap-svg';
+ this.containerId = 'resource-svg';
this.init = this.init.bind(this);
this.onDOMLoaded = this.onDOMLoaded.bind(this);
- this.fetchRoadmapSvg = this.fetchRoadmapSvg.bind(this);
- this.handleRoadmapClick = this.handleRoadmapClick.bind(this);
+ this.jsonToSvg = this.jsonToSvg.bind(this);
+ this.handleSvgClick = this.handleSvgClick.bind(this);
this.prepareConfig = this.prepareConfig.bind(this);
}
@@ -32,7 +26,8 @@ export class Renderer {
const dataset = this.containerEl.dataset;
- this.roadmapId = dataset.roadmapId;
+ this.resourceType = dataset.resourceType;
+ this.resourceId = dataset.resourceId;
this.jsonUrl = dataset.jsonUrl;
return true;
@@ -42,7 +37,7 @@ export class Renderer {
* @param { string } jsonUrl
* @returns {Promise}
*/
- fetchRoadmapSvg(jsonUrl) {
+ jsonToSvg(jsonUrl) {
if (!jsonUrl) {
console.error('jsonUrl not defined in frontmatter');
return null;
@@ -64,14 +59,14 @@ export class Renderer {
return;
}
- this.fetchRoadmapSvg(this.jsonUrl)
+ this.jsonToSvg(this.jsonUrl)
.then((svg) => {
document.getElementById(this.containerId).replaceChildren(svg);
})
.catch(console.error);
}
- handleRoadmapClick(e) {
+ handleSvgClick(e) {
const targetGroup = e.target.closest('g') || {};
const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : '';
if (!groupId) {
@@ -80,11 +75,19 @@ export class Renderer {
e.stopImmediatePropagation();
+ if (/^ext_link/.test(groupId)) {
+ window.open(`https://${groupId.replace('ext_link:', '')}`);
+ return;
+ }
+
+ // Remove sorting prefix from groupId
+ const normalizedGroupId = groupId.replace(/^\d+-/, '');
+
window.dispatchEvent(
- new CustomEvent('topic.click', {
+ new CustomEvent(`${this.resourceType}.topic.click`, {
detail: {
- topicId: groupId,
- roadmapId: this.roadmapId,
+ topicId: normalizedGroupId,
+ resourceId: this.resourceId,
},
})
);
@@ -92,7 +95,7 @@ export class Renderer {
init() {
window.addEventListener('DOMContentLoaded', this.onDOMLoaded);
- window.addEventListener('click', this.handleRoadmapClick);
+ window.addEventListener('click', this.handleSvgClick);
}
}
diff --git a/src/components/TopicOverlay/topic.js b/src/components/TopicOverlay/topic.js
index 971f142d8..c47b8a52a 100644
--- a/src/components/TopicOverlay/topic.js
+++ b/src/components/TopicOverlay/topic.js
@@ -10,10 +10,12 @@ export class Topic {
this.closeTopicId = 'close-topic';
this.contributionTextId = 'contrib-meta';
- this.activeRoadmapId = null;
+ this.activeResourceType = null;
+ this.activeResourceId = null;
this.activeTopicId = null;
- this.handleTopicClick = this.handleTopicClick.bind(this);
+ this.handleRoadmapTopicClick = this.handleRoadmapTopicClick.bind(this);
+ this.handleBestPracticeTopicClick = this.handleBestPracticeTopicClick.bind(this);
this.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this);
@@ -86,7 +88,7 @@ export class Topic {
close() {
this.resetDOM(true);
- this.activeRoadmapId = null;
+ this.activeResourceId = null;
this.activeTopicId = null;
}
@@ -115,11 +117,8 @@ export class Topic {
}
}
- fetchTopicHtml(roadmapId, topicId) {
- const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/');
- const fullUrl = `/${roadmapId}/${topicPartial}`;
-
- return fetch(fullUrl)
+ renderTopicFromUrl(url) {
+ return fetch(url)
.then((res) => {
return res.text();
})
@@ -129,33 +128,45 @@ export class Topic {
const node = new DOMParser().parseFromString(topicHtml, 'text/html');
return node.getElementById('main-content');
+ })
+ .then((content) => {
+ this.populate(content);
+ })
+ .catch((e) => {
+ console.error(e);
+ this.populate('Error loading the content!');
});
}
- handleTopicClick(e) {
- const { roadmapId, topicId } = e.detail;
- if (!topicId || !roadmapId) {
- console.log('Missing topic or roadmap: ', e.detail);
+ handleBestPracticeTopicClick(e) {
+ const { resourceId: bestPracticeId, topicId } = e.detail;
+ if (!topicId || !bestPracticeId) {
+ console.log('Missing topic or bestPracticeId: ', e.detail);
return;
}
- this.activeRoadmapId = roadmapId;
+ this.activeResourceType = 'best-practice';
+ this.activeResourceId = bestPracticeId;
this.activeTopicId = topicId;
- if (/^ext_link/.test(topicId)) {
- window.open(`https://${topicId.replace('ext_link:', '')}`);
+ this.resetDOM();
+
+ alert('Best practices are not yet implemented!');
+ }
+
+ handleRoadmapTopicClick(e) {
+ const { resourceId: roadmapId, topicId } = e.detail;
+ if (!topicId || !roadmapId) {
+ console.log('Missing topic or roadmap: ', e.detail);
return;
}
+ this.activeResourceType = 'roadmap';
+ this.activeResourceId = roadmapId;
+ this.activeTopicId = topicId;
+
this.resetDOM();
- this.fetchTopicHtml(roadmapId, topicId)
- .then((content) => {
- this.populate(content);
- })
- .catch((e) => {
- console.error(e);
- this.populate('Error loading the content!');
- });
+ this.renderTopicFromUrl(`/${roadmapId}/${topicId.replaceAll(':', '/')}`);
}
queryRoadmapElementsByTopicId(topicId) {
@@ -219,7 +230,8 @@ export class Topic {
}
init() {
- window.addEventListener('topic.click', this.handleTopicClick);
+ window.addEventListener('roadmap.topic.click', this.handleRoadmapTopicClick);
+ window.addEventListener('best-practice.topic.click', this.handleBestPracticeTopicClick);
window.addEventListener('click', this.handleOverlayClick);
window.addEventListener('contextmenu', this.rightClickListener);
diff --git a/src/pages/[roadmapId]/index.astro b/src/pages/[roadmapId]/index.astro
index 545aeee4a..d7c65426b 100644
--- a/src/pages/[roadmapId]/index.astro
+++ b/src/pages/[roadmapId]/index.astro
@@ -79,7 +79,12 @@ const contentContributionLink = `https://github.com/kamranahmedse/developer-road
-
+
)
}
diff --git a/src/pages/best-practices/[bestPracticeId].astro b/src/pages/best-practices/[bestPracticeId].astro
index c4a666003..f66b9cbfa 100644
--- a/src/pages/best-practices/[bestPracticeId].astro
+++ b/src/pages/best-practices/[bestPracticeId].astro
@@ -75,7 +75,8 @@ const contentContributionLink = `https://github.com/kamranahmedse/developer-road