mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-08 16:20:40 +02:00
Add download and subscribe popups
This commit is contained in:
47
src/components/DownloadPopup.astro
Normal file
47
src/components/DownloadPopup.astro
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
---
|
||||||
|
import Popup from "./Popup/Popup.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
id="download-popup"
|
||||||
|
title="Download Roadmap"
|
||||||
|
subtitle="Enter your email below to receive the download link."
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
action="https://newsletter.roadmap.sh/subscribe"
|
||||||
|
method="POST"
|
||||||
|
accept-charset="utf-8"
|
||||||
|
target="_blank"
|
||||||
|
class="validate-captcha-form"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
id="email"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
class="w-full rounded-md border text-md py-2.5 px-3 mb-2"
|
||||||
|
placeholder="Enter your Email"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- {# Captcha Form Start #} -->
|
||||||
|
<div class="recaptcha-field mb-2"></div>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="g-recaptcha-response"
|
||||||
|
class="recaptcha-response"
|
||||||
|
/>
|
||||||
|
<!-- {# Captcha Form End #} -->
|
||||||
|
|
||||||
|
<input type="hidden" name="list" value="tTqz1w7nexY3cWDpLnI88Q" />
|
||||||
|
<input type="hidden" name="subform" value="yes" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="submit"
|
||||||
|
class="text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2"
|
||||||
|
>
|
||||||
|
Send Link
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Popup>
|
@@ -1,6 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
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 TopicOverlay from "../TopicOverlay.astro";
|
import TopicOverlay from "../TopicOverlay.astro";
|
||||||
import "./InteractiveRoadmap.css";
|
import "./InteractiveRoadmap.css";
|
||||||
|
|
||||||
@@ -34,6 +36,8 @@ const { roadmapId, jsonUrl, dimensions, description, roadmapPermalink } =
|
|||||||
description={description}
|
description={description}
|
||||||
pageUrl={`https://roadmap.sh/${roadmapId}`}
|
pageUrl={`https://roadmap.sh/${roadmapId}`}
|
||||||
/>
|
/>
|
||||||
|
<DownloadPopup />
|
||||||
|
<SubscribePopup />
|
||||||
<TopicOverlay />
|
<TopicOverlay />
|
||||||
<div
|
<div
|
||||||
id="roadmap-svg"
|
id="roadmap-svg"
|
||||||
|
36
src/components/Popup/Popup.astro
Normal file
36
src/components/Popup/Popup.astro
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
import Icon from '../Icon.astro';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
subtitle: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { id, title, subtitle } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<script src="./popup.js"></script>
|
||||||
|
|
||||||
|
<div id={id} tabindex="-1" class="hidden bg-black/50 overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 h-full flex items-center justify-center modal">
|
||||||
|
<div class="relative p-4 w-full max-w-md h-full md:h-auto">
|
||||||
|
<div class="relative bg-white rounded-lg shadow modal-body">
|
||||||
|
<button type="button" class="absolute top-3 right-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm p-1.5 ml-auto inline-flex items-center" onclick='this.closest(".modal").classList.add("hidden")'>
|
||||||
|
<Icon icon="close" />
|
||||||
|
<span class="sr-only">Close modal</span>
|
||||||
|
</button>
|
||||||
|
<div class="p-5">
|
||||||
|
<h3 class='text-2xl mb-0.5 font-medium'>
|
||||||
|
{ title }
|
||||||
|
</h3>
|
||||||
|
<p class="mb-4 text-sm font-normal text-gray-800">
|
||||||
|
{ subtitle }
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
65
src/components/Popup/popup.js
Normal file
65
src/components/Popup/popup.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
export class Modal {
|
||||||
|
constructor() {
|
||||||
|
this.triggerModal = this.triggerModal.bind(this);
|
||||||
|
this.onDOMLoaded = this.onDOMLoaded.bind(this);
|
||||||
|
this.handleCloseModal = this.handleCloseModal.bind(this);
|
||||||
|
this.handleKeydown = this.handleKeydown.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers the modal on target elements
|
||||||
|
* @param {Event} e
|
||||||
|
*/
|
||||||
|
triggerModal(e) {
|
||||||
|
const modalToShow = e?.target?.closest('[data-modal]')?.dataset?.modal || 'unknown-modal';
|
||||||
|
const modalEl = document.querySelector(`#${modalToShow}`);
|
||||||
|
|
||||||
|
if (!modalEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
modalEl.classList.remove('hidden');
|
||||||
|
const focusEl = modalEl.querySelector('[autofocus]');
|
||||||
|
if (focusEl) {
|
||||||
|
focusEl.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCloseModal(e) {
|
||||||
|
const target = e.target;
|
||||||
|
const modalBody = target.closest('.modal-body');
|
||||||
|
const closestModal = target.closest('.modal');
|
||||||
|
|
||||||
|
if (modalBody) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closestModal) {
|
||||||
|
closestModal.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleKeydown(e) {
|
||||||
|
if (e.key !== 'Escape') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const modal = document.querySelector('.modal:not(.hidden)');
|
||||||
|
if (modal) {
|
||||||
|
modal.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onDOMLoaded() {
|
||||||
|
document.addEventListener('click', this.triggerModal);
|
||||||
|
document.addEventListener('click', this.handleCloseModal);
|
||||||
|
document.addEventListener('keydown', this.handleKeydown);
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
window.addEventListener('DOMContentLoaded', this.onDOMLoaded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const modalRef = new Modal();
|
||||||
|
modalRef.init();
|
46
src/components/SubscribePopup.astro
Normal file
46
src/components/SubscribePopup.astro
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
import Popup from "./Popup/Popup.astro";
|
||||||
|
---
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
id="subscribe-popup"
|
||||||
|
title="Subscribe"
|
||||||
|
subtitle="Enter your email below to receive updates."
|
||||||
|
>
|
||||||
|
<form
|
||||||
|
action="https://newsletter.roadmap.sh/subscribe"
|
||||||
|
method="POST"
|
||||||
|
accept-charset="utf-8"
|
||||||
|
target="_blank"
|
||||||
|
class="validate-captcha-form"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
name="email"
|
||||||
|
required
|
||||||
|
autofocus
|
||||||
|
class="w-full rounded-md border text-md py-2.5 px-3 mb-2"
|
||||||
|
placeholder="Enter your Email"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Captcha Form Start -->
|
||||||
|
<div class="recaptcha-field mb-2"></div>
|
||||||
|
<input
|
||||||
|
type="hidden"
|
||||||
|
name="g-recaptcha-response"
|
||||||
|
class="recaptcha-response"
|
||||||
|
/>
|
||||||
|
<!-- Captcha Form End -->
|
||||||
|
|
||||||
|
<input type="hidden" name="list" value="tTqz1w7nexY3cWDpLnI88Q" />
|
||||||
|
<input type="hidden" name="subform" value="yes" />
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="submit"
|
||||||
|
class="text-white bg-gradient-to-r from-amber-700 to-blue-800 hover:from-amber-800 hover:to-blue-900 font-regular rounded-md text-md px-5 py-2.5 w-full text-center mr-2"
|
||||||
|
>
|
||||||
|
Subscribe
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</Popup>
|
Reference in New Issue
Block a user