From c4406b76494c915bf70036e184d36b556f616c47 Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Tue, 10 Jan 2023 19:39:43 +0400 Subject: [PATCH] Add meta text below roadmap topic for contribution --- .../InteractiveRoadmap.astro | 39 +- src/components/InteractiveRoadmap/topic.js | 407 +++++++++--------- src/components/TopicOverlay.astro | 86 ++-- 3 files changed, 293 insertions(+), 239 deletions(-) diff --git a/src/components/InteractiveRoadmap/InteractiveRoadmap.astro b/src/components/InteractiveRoadmap/InteractiveRoadmap.astro index 3d0746289..2bb16fef8 100644 --- a/src/components/InteractiveRoadmap/InteractiveRoadmap.astro +++ b/src/components/InteractiveRoadmap/InteractiveRoadmap.astro @@ -1,10 +1,10 @@ --- -import DownloadPopup from "../DownloadPopup.astro"; -import Loader from "../Loader.astro"; -import ShareIcons from "../ShareIcons.astro"; -import SubscribePopup from "../SubscribePopup.astro"; -import TopicOverlay from "../TopicOverlay.astro"; -import "./InteractiveRoadmap.css"; +import DownloadPopup from '../DownloadPopup.astro'; +import Loader from '../Loader.astro'; +import ShareIcons from '../ShareIcons.astro'; +import SubscribePopup from '../SubscribePopup.astro'; +import TopicOverlay from '../TopicOverlay.astro'; +import './InteractiveRoadmap.css'; export interface Props { roadmapId: string; @@ -16,31 +16,32 @@ export interface Props { }; } -const { roadmapId, jsonUrl, dimensions = null, description } = - Astro.props; +const { roadmapId, jsonUrl, dimensions = null, description } = Astro.props; --- -
-
+
+
- +
@@ -49,4 +50,4 @@ const { roadmapId, jsonUrl, dimensions = null, description } =
- + diff --git a/src/components/InteractiveRoadmap/topic.js b/src/components/InteractiveRoadmap/topic.js index 8a481f258..ef6b7e767 100644 --- a/src/components/InteractiveRoadmap/topic.js +++ b/src/components/InteractiveRoadmap/topic.js @@ -1,204 +1,219 @@ export class Topic { - constructor() { - this.overlayId = 'topic-overlay'; - this.contentId = 'topic-content'; - this.loaderId = 'topic-loader'; - this.topicBodyId = 'topic-body'; - this.topicActionsId = 'topic-actions'; - this.markTopicDoneId = 'mark-topic-done'; - this.markTopicPendingId = 'mark-topic-pending'; - this.closeTopicId = 'close-topic'; - - this.activeRoadmapId = null; - this.activeTopicId = null; - - this.handleTopicClick = this.handleTopicClick.bind(this); - - this.close = this.close.bind(this); - this.resetDOM = this.resetDOM.bind(this); - this.populate = this.populate.bind(this); - this.handleOverlayClick = this.handleOverlayClick.bind(this); - this.markAsDone = this.markAsDone.bind(this); - this.markAsPending = this.markAsPending.bind(this); - this.queryRoadmapElementsByTopicId = this.queryRoadmapElementsByTopicId.bind(this); - - this.init = this.init.bind(this); - } - - get loaderEl() { - return document.getElementById(this.loaderId); - } - - get markTopicDoneEl() { - return document.getElementById(this.markTopicDoneId); - } - - get markTopicPendingEl() { - return document.getElementById(this.markTopicPendingId); - } - - get topicActionsEl() { - return document.getElementById(this.topicActionsId); - } - - get contentEl() { - return document.getElementById(this.contentId); - } - - get overlayEl() { - return document.getElementById(this.overlayId); - } - - resetDOM(hideOverlay = false) { - if (hideOverlay) { - this.overlayEl.classList.add('hidden'); - } else { - this.overlayEl.classList.remove('hidden'); - } - - this.loaderEl.classList.remove('hidden'); // Show loader - this.topicActionsEl.classList.add('hidden'); // Hide Actions - this.contentEl.replaceChildren(''); // Remove content - } - - close() { - this.resetDOM(true); - - this.activeRoadmapId = null; - this.activeTopicId = null; - } - - /** - * @param {string | HTMLElement} html - */ - populate(html) { - this.contentEl.replaceChildren(html); - this.loaderEl.classList.add('hidden'); - this.topicActionsEl.classList.remove('hidden'); - - const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, ''); - const isDone = localStorage.getItem(normalizedGroup) === 'done'; - - if (isDone) { - this.markTopicDoneEl.classList.add('hidden'); - this.markTopicPendingEl.classList.remove('hidden'); - } else { - this.markTopicDoneEl.classList.remove('hidden'); - this.markTopicPendingEl.classList.add('hidden'); - } - } - - fetchTopicHtml(roadmapId, topicId) { - const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/'); - const fullUrl = `/${roadmapId}/${topicPartial}/`; - - return fetch(fullUrl) - .then((res) => { - return res.text(); - }) - .then((topicHtml) => { - // It's full HTML with page body, head etc. - // We only need the inner HTML of the #main-content - const node = new DOMParser().parseFromString(topicHtml, 'text/html'); - - return node.getElementById('main-content'); - }); - } - - handleTopicClick(e) { - const { roadmapId, topicId } = e.detail; - if (!topicId || !roadmapId) { - console.log('Missing topic or roadmap: ', e.detail); - return; - } - - this.activeRoadmapId = roadmapId; - this.activeTopicId = topicId; - - if (/^ext_link/.test(topicId)) { - window.open(`https://${topicId.replace('ext_link:', '')}`); - return; - } - - this.resetDOM(); - this.fetchTopicHtml(roadmapId, topicId) - .then((content) => { - this.populate(content); - }) - .catch((e) => { - console.error(e); - this.populate('Error loading the content!'); - }); - } - - queryRoadmapElementsByTopicId(topicId) { - const elements = document.querySelectorAll(`[data-group-id$="-${topicId}"]`); - const matchingElements = []; - - elements.forEach((element) => { - const foundGroupId = element?.dataset?.groupId || ''; - const validGroupRegex = new RegExp(`^\\d+-${topicId}$`); - - if (validGroupRegex.test(foundGroupId)) { - matchingElements.push(element); - } - }); - - return matchingElements; + constructor() { + this.overlayId = 'topic-overlay'; + this.contentId = 'topic-content'; + this.loaderId = 'topic-loader'; + this.topicBodyId = 'topic-body'; + this.topicActionsId = 'topic-actions'; + this.markTopicDoneId = 'mark-topic-done'; + this.markTopicPendingId = 'mark-topic-pending'; + this.closeTopicId = 'close-topic'; + this.contributionTextId = 'contrib-meta'; + + this.activeRoadmapId = null; + this.activeTopicId = null; + + this.handleTopicClick = this.handleTopicClick.bind(this); + + this.close = this.close.bind(this); + this.resetDOM = this.resetDOM.bind(this); + this.populate = this.populate.bind(this); + this.handleOverlayClick = this.handleOverlayClick.bind(this); + this.markAsDone = this.markAsDone.bind(this); + this.markAsPending = this.markAsPending.bind(this); + this.queryRoadmapElementsByTopicId = + this.queryRoadmapElementsByTopicId.bind(this); + + this.init = this.init.bind(this); + } + + get loaderEl() { + return document.getElementById(this.loaderId); + } + + get markTopicDoneEl() { + return document.getElementById(this.markTopicDoneId); + } + + get markTopicPendingEl() { + return document.getElementById(this.markTopicPendingId); + } + + get topicActionsEl() { + return document.getElementById(this.topicActionsId); + } + + get contributionTextEl() { + return document.getElementById(this.contributionTextId); + } + + get contentEl() { + return document.getElementById(this.contentId); + } + + get overlayEl() { + return document.getElementById(this.overlayId); + } + + resetDOM(hideOverlay = false) { + if (hideOverlay) { + this.overlayEl.classList.add('hidden'); + } else { + this.overlayEl.classList.remove('hidden'); + } + + this.loaderEl.classList.remove('hidden'); // Show loader + this.topicActionsEl.classList.add('hidden'); // Hide Actions + this.contributionTextEl.classList.add('hidden'); // Hide contribution text + this.contentEl.replaceChildren(''); // Remove content + } + + close() { + this.resetDOM(true); + + this.activeRoadmapId = null; + this.activeTopicId = null; + } + + /** + * @param {string | HTMLElement} html + */ + populate(html) { + this.contentEl.replaceChildren(html); + this.loaderEl.classList.add('hidden'); + this.topicActionsEl.classList.remove('hidden'); + this.contributionTextEl.classList.remove('hidden'); + + const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, ''); + const isDone = localStorage.getItem(normalizedGroup) === 'done'; + + if (isDone) { + this.markTopicDoneEl.classList.add('hidden'); + this.markTopicPendingEl.classList.remove('hidden'); + } else { + this.markTopicDoneEl.classList.remove('hidden'); + this.markTopicPendingEl.classList.add('hidden'); } - - markAsDone(topicId) { - const updatedTopicId = topicId.replace(/^\d+-/, ''); - localStorage.setItem(updatedTopicId, 'done'); - - this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => { - item?.classList?.add('done'); + } + + fetchTopicHtml(roadmapId, topicId) { + const topicPartial = topicId.replace(/^\d+-/, '').replaceAll(/:/g, '/'); + const fullUrl = `/${roadmapId}/${topicPartial}/`; + + return fetch(fullUrl) + .then((res) => { + return res.text(); + }) + .then((topicHtml) => { + // It's full HTML with page body, head etc. + // We only need the inner HTML of the #main-content + const node = new DOMParser().parseFromString(topicHtml, 'text/html'); + + return node.getElementById('main-content'); }); - } - - markAsPending(topicId) { - const updatedTopicId = topicId.replace(/^\d+-/, ''); - - localStorage.removeItem(updatedTopicId); - this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => { - item?.classList?.remove('done'); + } + + handleTopicClick(e) { + const { roadmapId, topicId } = e.detail; + if (!topicId || !roadmapId) { + console.log('Missing topic or roadmap: ', e.detail); + return; + } + + this.activeRoadmapId = roadmapId; + this.activeTopicId = topicId; + + if (/^ext_link/.test(topicId)) { + window.open(`https://${topicId.replace('ext_link:', '')}`); + return; + } + + this.resetDOM(); + this.fetchTopicHtml(roadmapId, topicId) + .then((content) => { + this.populate(content); + }) + .catch((e) => { + console.error(e); + this.populate('Error loading the content!'); }); - } - - handleOverlayClick(e) { - const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`); - - if (!isClickedInsideTopic) { - this.close(); - return; - } - - const isClickedDone = e.target.id === this.markTopicDoneId || e.target.closest(`#${this.markTopicDoneId}`); - if (isClickedDone) { - this.markAsDone(this.activeTopicId); - this.close(); - } - - const isClickedPending = e.target.id === this.markTopicPendingId || e.target.closest(`#${this.markTopicPendingId}`); - if (isClickedPending) { - this.markAsPending(this.activeTopicId); - this.close(); + } + + queryRoadmapElementsByTopicId(topicId) { + const elements = document.querySelectorAll( + `[data-group-id$="-${topicId}"]` + ); + const matchingElements = []; + + elements.forEach((element) => { + const foundGroupId = element?.dataset?.groupId || ''; + const validGroupRegex = new RegExp(`^\\d+-${topicId}$`); + + if (validGroupRegex.test(foundGroupId)) { + matchingElements.push(element); } - - const isClickedClose = e.target.id === this.closeTopicId || e.target.closest(`#${this.closeTopicId}`); - if (isClickedClose) { + }); + + return matchingElements; + } + + markAsDone(topicId) { + const updatedTopicId = topicId.replace(/^\d+-/, ''); + localStorage.setItem(updatedTopicId, 'done'); + + this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => { + item?.classList?.add('done'); + }); + } + + markAsPending(topicId) { + const updatedTopicId = topicId.replace(/^\d+-/, ''); + + localStorage.removeItem(updatedTopicId); + this.queryRoadmapElementsByTopicId(updatedTopicId).forEach((item) => { + item?.classList?.remove('done'); + }); + } + + handleOverlayClick(e) { + const isClickedInsideTopic = e.target.closest(`#${this.topicBodyId}`); + + if (!isClickedInsideTopic) { + this.close(); + return; + } + + const isClickedDone = + e.target.id === this.markTopicDoneId || + e.target.closest(`#${this.markTopicDoneId}`); + if (isClickedDone) { + this.markAsDone(this.activeTopicId); + this.close(); + } + + const isClickedPending = + e.target.id === this.markTopicPendingId || + e.target.closest(`#${this.markTopicPendingId}`); + if (isClickedPending) { + this.markAsPending(this.activeTopicId); + this.close(); + } + + const isClickedClose = + e.target.id === this.closeTopicId || + e.target.closest(`#${this.closeTopicId}`); + if (isClickedClose) { + this.close(); + } + } + + init() { + window.addEventListener('topic.click', this.handleTopicClick); + window.addEventListener('click', this.handleOverlayClick); + window.addEventListener('keydown', (e) => { + if (e.key.toLowerCase() === 'escape') { this.close(); } - } - - init() { - window.addEventListener('topic.click', this.handleTopicClick); - window.addEventListener('click', this.handleOverlayClick); - window.addEventListener('keydown', (e) => { - if (e.key.toLowerCase() === 'escape') { - this.close(); - } - }); - } + }); } - \ No newline at end of file +} diff --git a/src/components/TopicOverlay.astro b/src/components/TopicOverlay.astro index 160fba5a4..b1c43de60 100644 --- a/src/components/TopicOverlay.astro +++ b/src/components/TopicOverlay.astro @@ -1,31 +1,69 @@ --- -import Icon from "./Icon.astro"; -import Loader from "./Loader.astro"; +import Icon from './Icon.astro'; +import Loader from './Loader.astro'; +export interface Props { + roadmapId: string; +} + +const { roadmapId } = Astro.props; +const githubLink = `https://github.com/kamranahmedse/developer-roadmap/tree/master/src/roadmaps/${roadmapId}/content`; ---