mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-06 17:26:29 +02:00
Add meta text below roadmap topic for contribution
This commit is contained in:
@@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
import DownloadPopup from "../DownloadPopup.astro";
|
import DownloadPopup from '../DownloadPopup.astro';
|
||||||
import Loader from "../Loader.astro";
|
import Loader from '../Loader.astro';
|
||||||
import ShareIcons from "../ShareIcons.astro";
|
import ShareIcons from '../ShareIcons.astro';
|
||||||
import SubscribePopup from "../SubscribePopup.astro";
|
import SubscribePopup from '../SubscribePopup.astro';
|
||||||
import TopicOverlay from "../TopicOverlay.astro";
|
import TopicOverlay from '../TopicOverlay.astro';
|
||||||
import "./InteractiveRoadmap.css";
|
import './InteractiveRoadmap.css';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
roadmapId: string;
|
roadmapId: string;
|
||||||
@@ -16,31 +16,32 @@ export interface Props {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const { roadmapId, jsonUrl, dimensions = null, description } =
|
const { roadmapId, jsonUrl, dimensions = null, description } = Astro.props;
|
||||||
Astro.props;
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<link
|
<link
|
||||||
rel="preload"
|
rel='preload'
|
||||||
href="/fonts/balsamiq.woff2"
|
href='/fonts/balsamiq.woff2'
|
||||||
as="font"
|
as='font'
|
||||||
type="font/woff2"
|
type='font/woff2'
|
||||||
crossorigin
|
crossorigin
|
||||||
slot="after-header"
|
slot='after-header'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="bg-gray-50 py-4 sm:py-12">
|
<div class='bg-gray-50 py-4 sm:py-12'>
|
||||||
<div class="max-w-[1000px] container relative">
|
<div class='max-w-[1000px] container relative'>
|
||||||
<ShareIcons
|
<ShareIcons
|
||||||
description={description}
|
description={description}
|
||||||
pageUrl={`https://roadmap.sh/${roadmapId}`}
|
pageUrl={`https://roadmap.sh/${roadmapId}`}
|
||||||
/>
|
/>
|
||||||
<DownloadPopup />
|
<DownloadPopup />
|
||||||
<SubscribePopup />
|
<SubscribePopup />
|
||||||
<TopicOverlay />
|
<TopicOverlay roadmapId={roadmapId} />
|
||||||
<div
|
<div
|
||||||
id="roadmap-svg"
|
id='roadmap-svg'
|
||||||
style={dimensions ? `--aspect-ratio:${dimensions.width}/${dimensions.height}` : null}
|
style={dimensions
|
||||||
|
? `--aspect-ratio:${dimensions.width}/${dimensions.height}`
|
||||||
|
: null}
|
||||||
data-roadmap-id={roadmapId}
|
data-roadmap-id={roadmapId}
|
||||||
data-json-url={jsonUrl}
|
data-json-url={jsonUrl}
|
||||||
>
|
>
|
||||||
@@ -49,4 +50,4 @@ const { roadmapId, jsonUrl, dimensions = null, description } =
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="./roadmap.js"></script>
|
<script src='./roadmap.js'></script>
|
||||||
|
@@ -1,204 +1,219 @@
|
|||||||
export class Topic {
|
export class Topic {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.overlayId = 'topic-overlay';
|
this.overlayId = 'topic-overlay';
|
||||||
this.contentId = 'topic-content';
|
this.contentId = 'topic-content';
|
||||||
this.loaderId = 'topic-loader';
|
this.loaderId = 'topic-loader';
|
||||||
this.topicBodyId = 'topic-body';
|
this.topicBodyId = 'topic-body';
|
||||||
this.topicActionsId = 'topic-actions';
|
this.topicActionsId = 'topic-actions';
|
||||||
this.markTopicDoneId = 'mark-topic-done';
|
this.markTopicDoneId = 'mark-topic-done';
|
||||||
this.markTopicPendingId = 'mark-topic-pending';
|
this.markTopicPendingId = 'mark-topic-pending';
|
||||||
this.closeTopicId = 'close-topic';
|
this.closeTopicId = 'close-topic';
|
||||||
|
this.contributionTextId = 'contrib-meta';
|
||||||
this.activeRoadmapId = null;
|
|
||||||
this.activeTopicId = null;
|
this.activeRoadmapId = null;
|
||||||
|
this.activeTopicId = null;
|
||||||
this.handleTopicClick = this.handleTopicClick.bind(this);
|
|
||||||
|
this.handleTopicClick = this.handleTopicClick.bind(this);
|
||||||
this.close = this.close.bind(this);
|
|
||||||
this.resetDOM = this.resetDOM.bind(this);
|
this.close = this.close.bind(this);
|
||||||
this.populate = this.populate.bind(this);
|
this.resetDOM = this.resetDOM.bind(this);
|
||||||
this.handleOverlayClick = this.handleOverlayClick.bind(this);
|
this.populate = this.populate.bind(this);
|
||||||
this.markAsDone = this.markAsDone.bind(this);
|
this.handleOverlayClick = this.handleOverlayClick.bind(this);
|
||||||
this.markAsPending = this.markAsPending.bind(this);
|
this.markAsDone = this.markAsDone.bind(this);
|
||||||
this.queryRoadmapElementsByTopicId = this.queryRoadmapElementsByTopicId.bind(this);
|
this.markAsPending = this.markAsPending.bind(this);
|
||||||
|
this.queryRoadmapElementsByTopicId =
|
||||||
this.init = this.init.bind(this);
|
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
get loaderEl() {
|
this.loaderEl.classList.remove('hidden'); // Show loader
|
||||||
return document.getElementById(this.loaderId);
|
this.topicActionsEl.classList.add('hidden'); // Hide Actions
|
||||||
}
|
this.contributionTextEl.classList.add('hidden'); // Hide contribution text
|
||||||
|
this.contentEl.replaceChildren(''); // Remove content
|
||||||
get markTopicDoneEl() {
|
}
|
||||||
return document.getElementById(this.markTopicDoneId);
|
|
||||||
}
|
close() {
|
||||||
|
this.resetDOM(true);
|
||||||
get markTopicPendingEl() {
|
|
||||||
return document.getElementById(this.markTopicPendingId);
|
this.activeRoadmapId = null;
|
||||||
}
|
this.activeTopicId = null;
|
||||||
|
}
|
||||||
get topicActionsEl() {
|
|
||||||
return document.getElementById(this.topicActionsId);
|
/**
|
||||||
}
|
* @param {string | HTMLElement} html
|
||||||
|
*/
|
||||||
get contentEl() {
|
populate(html) {
|
||||||
return document.getElementById(this.contentId);
|
this.contentEl.replaceChildren(html);
|
||||||
}
|
this.loaderEl.classList.add('hidden');
|
||||||
|
this.topicActionsEl.classList.remove('hidden');
|
||||||
get overlayEl() {
|
this.contributionTextEl.classList.remove('hidden');
|
||||||
return document.getElementById(this.overlayId);
|
|
||||||
}
|
const normalizedGroup = (this.activeTopicId || '').replace(/^\d+-/, '');
|
||||||
|
const isDone = localStorage.getItem(normalizedGroup) === 'done';
|
||||||
resetDOM(hideOverlay = false) {
|
|
||||||
if (hideOverlay) {
|
if (isDone) {
|
||||||
this.overlayEl.classList.add('hidden');
|
this.markTopicDoneEl.classList.add('hidden');
|
||||||
} else {
|
this.markTopicPendingEl.classList.remove('hidden');
|
||||||
this.overlayEl.classList.remove('hidden');
|
} else {
|
||||||
}
|
this.markTopicDoneEl.classList.remove('hidden');
|
||||||
|
this.markTopicPendingEl.classList.add('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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,31 +1,69 @@
|
|||||||
---
|
---
|
||||||
import Icon from "./Icon.astro";
|
import Icon from './Icon.astro';
|
||||||
import Loader from "./Loader.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`;
|
||||||
---
|
---
|
||||||
|
|
||||||
<div id='topic-overlay' class='hidden'>
|
<div id='topic-overlay' class='hidden'>
|
||||||
<div class="fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]" tabindex="-1" id='topic-body'>
|
<div
|
||||||
<div id='topic-loader' class='hidden'>
|
class='fixed top-0 right-0 z-40 h-screen p-4 sm:p-6 overflow-y-auto bg-white w-full sm:max-w-[600px]'
|
||||||
<Loader />
|
tabindex='-1'
|
||||||
</div>
|
id='topic-body'
|
||||||
|
>
|
||||||
<div id='topic-actions' class='hidden mb-2'>
|
<div id='topic-loader' class='hidden'>
|
||||||
<button id='mark-topic-done' class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'>
|
<Loader />
|
||||||
<Icon icon="check" /> <span class='ml-2'>Mark as Done</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button id='mark-topic-pending' class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'>
|
|
||||||
<Icon icon="reset" /> <span class='ml-2'>Mark as Pending</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button type="button" id='close-topic' class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center">
|
|
||||||
<Icon icon="close" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id='topic-content' class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30"></div>
|
|
||||||
|
<div id='topic-actions' class='hidden mb-2'>
|
||||||
|
<button
|
||||||
|
id='mark-topic-done'
|
||||||
|
class='bg-green-600 text-white p-1 px-2 text-sm rounded-md hover:bg-green-700 inline-flex items-center'
|
||||||
|
>
|
||||||
|
<Icon icon='check' />
|
||||||
|
<span class='ml-2'>Mark as Done</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
id='mark-topic-pending'
|
||||||
|
class='hidden bg-red-600 text-white p-1 px-2 text-sm rounded-md hover:bg-red-700 inline-flex items-center'
|
||||||
|
>
|
||||||
|
<Icon icon='reset' />
|
||||||
|
<span class='ml-2'>Mark as Pending</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
id='close-topic'
|
||||||
|
class='text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 absolute top-2.5 right-2.5 inline-flex items-center'
|
||||||
|
>
|
||||||
|
<Icon icon='close' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
id='topic-content'
|
||||||
|
class='prose prose-h1:mt-7 prose-h1:mb-2.5 prose-p:mt-0 prose-p:mb-2 prose-li:m-0 prose-li:mb-0.5 prose-h2:mb-3 prose-h2:mt-0'
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p
|
||||||
|
id='contrib-meta'
|
||||||
|
class='text-gray-400 text-sm border-t pt-3 mt-10 hidden'
|
||||||
|
>
|
||||||
|
We are still working on this page. You can contribute by submitting a
|
||||||
|
brief description and a few links to learn more about this topic <a
|
||||||
|
target='_blank'
|
||||||
|
class='underline text-blue-700'
|
||||||
|
href={githubLink}>on GitHub repository.</a
|
||||||
|
>.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div class='bg-gray-900 bg-opacity-50 dark:bg-opacity-80 fixed inset-0 z-30'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
Reference in New Issue
Block a user