Handle rendering of the roadmap topics

best-practices
Kamran Ahmed 2 years ago
parent dbcf06244b
commit 1928b89d71
  1. 10
      src/components/FrameRenderer/FrameRenderer.astro
  2. 41
      src/components/FrameRenderer/renderer.js
  3. 60
      src/components/TopicOverlay/topic.js
  4. 7
      src/pages/[roadmapId]/index.astro
  5. 3
      src/pages/best-practices/[bestPracticeId].astro

@ -4,7 +4,8 @@ import TopicOverlay from '../TopicOverlay/TopicOverlay.astro';
import './FrameRenderer.css'; import './FrameRenderer.css';
export interface Props { export interface Props {
roadmapId: string; resourceType: 'roadmap' | 'best-practice';
resourceId: string;
jsonUrl: string; jsonUrl: string;
dimensions?: { dimensions?: {
width: number; width: number;
@ -12,13 +13,14 @@ export interface Props {
}; };
} }
const { roadmapId, jsonUrl, dimensions = null } = Astro.props; const { resourceId, resourceType, jsonUrl, dimensions = null } = Astro.props;
--- ---
<div <div
id='roadmap-svg' id='resource-svg'
style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null} style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null}
data-roadmap-id={roadmapId} data-resource-type={resourceType}
data-resource-id={resourceId}
data-json-url={jsonUrl} data-json-url={jsonUrl}
> >
<Loader /> <Loader />

@ -1,23 +1,17 @@
import { wireframeJSONToSVG } from 'roadmap-renderer'; import { wireframeJSONToSVG } from 'roadmap-renderer';
/**
* @typedef {{ roadmapId: string, jsonUrl: string }} RoadmapConfig
*/
export class Renderer { export class Renderer {
/**
* @param {RoadmapConfig} config
*/
constructor() { constructor() {
this.roadmapId = ''; this.resourceId = '';
this.resourceType = '';
this.jsonUrl = ''; this.jsonUrl = '';
this.containerId = 'roadmap-svg'; this.containerId = 'resource-svg';
this.init = this.init.bind(this); this.init = this.init.bind(this);
this.onDOMLoaded = this.onDOMLoaded.bind(this); this.onDOMLoaded = this.onDOMLoaded.bind(this);
this.fetchRoadmapSvg = this.fetchRoadmapSvg.bind(this); this.jsonToSvg = this.jsonToSvg.bind(this);
this.handleRoadmapClick = this.handleRoadmapClick.bind(this); this.handleSvgClick = this.handleSvgClick.bind(this);
this.prepareConfig = this.prepareConfig.bind(this); this.prepareConfig = this.prepareConfig.bind(this);
} }
@ -32,7 +26,8 @@ export class Renderer {
const dataset = this.containerEl.dataset; const dataset = this.containerEl.dataset;
this.roadmapId = dataset.roadmapId; this.resourceType = dataset.resourceType;
this.resourceId = dataset.resourceId;
this.jsonUrl = dataset.jsonUrl; this.jsonUrl = dataset.jsonUrl;
return true; return true;
@ -42,7 +37,7 @@ export class Renderer {
* @param { string } jsonUrl * @param { string } jsonUrl
* @returns {Promise<SVGElement>} * @returns {Promise<SVGElement>}
*/ */
fetchRoadmapSvg(jsonUrl) { jsonToSvg(jsonUrl) {
if (!jsonUrl) { if (!jsonUrl) {
console.error('jsonUrl not defined in frontmatter'); console.error('jsonUrl not defined in frontmatter');
return null; return null;
@ -64,14 +59,14 @@ export class Renderer {
return; return;
} }
this.fetchRoadmapSvg(this.jsonUrl) this.jsonToSvg(this.jsonUrl)
.then((svg) => { .then((svg) => {
document.getElementById(this.containerId).replaceChildren(svg); document.getElementById(this.containerId).replaceChildren(svg);
}) })
.catch(console.error); .catch(console.error);
} }
handleRoadmapClick(e) { handleSvgClick(e) {
const targetGroup = e.target.closest('g') || {}; const targetGroup = e.target.closest('g') || {};
const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : ''; const groupId = targetGroup.dataset ? targetGroup.dataset.groupId : '';
if (!groupId) { if (!groupId) {
@ -80,11 +75,19 @@ export class Renderer {
e.stopImmediatePropagation(); 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( window.dispatchEvent(
new CustomEvent('topic.click', { new CustomEvent(`${this.resourceType}.topic.click`, {
detail: { detail: {
topicId: groupId, topicId: normalizedGroupId,
roadmapId: this.roadmapId, resourceId: this.resourceId,
}, },
}) })
); );
@ -92,7 +95,7 @@ export class Renderer {
init() { init() {
window.addEventListener('DOMContentLoaded', this.onDOMLoaded); window.addEventListener('DOMContentLoaded', this.onDOMLoaded);
window.addEventListener('click', this.handleRoadmapClick); window.addEventListener('click', this.handleSvgClick);
} }
} }

@ -10,10 +10,12 @@ export class Topic {
this.closeTopicId = 'close-topic'; this.closeTopicId = 'close-topic';
this.contributionTextId = 'contrib-meta'; this.contributionTextId = 'contrib-meta';
this.activeRoadmapId = null; this.activeResourceType = null;
this.activeResourceId = null;
this.activeTopicId = 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.close = this.close.bind(this);
this.resetDOM = this.resetDOM.bind(this); this.resetDOM = this.resetDOM.bind(this);
@ -86,7 +88,7 @@ export class Topic {
close() { close() {
this.resetDOM(true); this.resetDOM(true);
this.activeRoadmapId = null; this.activeResourceId = null;
this.activeTopicId = null; this.activeTopicId = null;
} }
@ -115,11 +117,8 @@ export class Topic {
} }
} }
fetchTopicHtml(roadmapId, topicId) { renderTopicFromUrl(url) {
const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/'); return fetch(url)
const fullUrl = `/${roadmapId}/${topicPartial}`;
return fetch(fullUrl)
.then((res) => { .then((res) => {
return res.text(); return res.text();
}) })
@ -129,33 +128,45 @@ export class Topic {
const node = new DOMParser().parseFromString(topicHtml, 'text/html'); const node = new DOMParser().parseFromString(topicHtml, 'text/html');
return node.getElementById('main-content'); return node.getElementById('main-content');
})
.then((content) => {
this.populate(content);
})
.catch((e) => {
console.error(e);
this.populate('Error loading the content!');
}); });
} }
handleTopicClick(e) { handleBestPracticeTopicClick(e) {
const { roadmapId, topicId } = e.detail; const { resourceId: bestPracticeId, topicId } = e.detail;
if (!topicId || !roadmapId) { if (!topicId || !bestPracticeId) {
console.log('Missing topic or roadmap: ', e.detail); console.log('Missing topic or bestPracticeId: ', e.detail);
return; return;
} }
this.activeRoadmapId = roadmapId; this.activeResourceType = 'best-practice';
this.activeResourceId = bestPracticeId;
this.activeTopicId = topicId; this.activeTopicId = topicId;
if (/^ext_link/.test(topicId)) { this.resetDOM();
window.open(`https://${topicId.replace('ext_link:', '')}`);
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; return;
} }
this.activeResourceType = 'roadmap';
this.activeResourceId = roadmapId;
this.activeTopicId = topicId;
this.resetDOM(); this.resetDOM();
this.fetchTopicHtml(roadmapId, topicId) this.renderTopicFromUrl(`/${roadmapId}/${topicId.replaceAll(':', '/')}`);
.then((content) => {
this.populate(content);
})
.catch((e) => {
console.error(e);
this.populate('Error loading the content!');
});
} }
queryRoadmapElementsByTopicId(topicId) { queryRoadmapElementsByTopicId(topicId) {
@ -219,7 +230,8 @@ export class Topic {
} }
init() { 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('click', this.handleOverlayClick);
window.addEventListener('contextmenu', this.rightClickListener); window.addEventListener('contextmenu', this.rightClickListener);

@ -79,7 +79,12 @@ const contentContributionLink = `https://github.com/kamranahmedse/developer-road
<ShareIcons description={roadmapData.featuredDescription} pageUrl={`https://roadmap.sh/${roadmapId}`} /> <ShareIcons description={roadmapData.featuredDescription} pageUrl={`https://roadmap.sh/${roadmapId}`} />
<TopicOverlay contentContributionLink={contentContributionLink} /> <TopicOverlay contentContributionLink={contentContributionLink} />
<FrameRenderer roadmapId={roadmapId} jsonUrl={roadmapData.jsonUrl} dimensions={roadmapData.dimensions} /> <FrameRenderer
resourceType={'roadmap'}
resourceId={roadmapId}
jsonUrl={roadmapData.jsonUrl}
dimensions={roadmapData.dimensions}
/>
</div> </div>
) )
} }

@ -75,7 +75,8 @@ const contentContributionLink = `https://github.com/kamranahmedse/developer-road
<TopicOverlay contentContributionLink={contentContributionLink} /> <TopicOverlay contentContributionLink={contentContributionLink} />
<FrameRenderer <FrameRenderer
roadmapId={bestPracticeId} resourceType={'best-practice'}
resourceId={bestPracticeId}
jsonUrl={bestPracticeData.jsonUrl} jsonUrl={bestPracticeData.jsonUrl}
dimensions={bestPracticeData.dimensions} dimensions={bestPracticeData.dimensions}
/> />

Loading…
Cancel
Save