diff --git a/src/components/ToastSubscribed/index.js b/src/components/ToastSubscribed/index.js new file mode 100644 index 00000000000..a9e719572d3 --- /dev/null +++ b/src/components/ToastSubscribed/index.js @@ -0,0 +1,34 @@ +import React from "react"; +import Admonition from "@theme/Admonition"; +import IconCheck from "@site/static/img/icon-check.svg"; + +const ToastSubscribed = () => { + return ( + } + title="You're subscribed" + className="toast" + > + You’ll now receive our Changelog updates by email. + + document.querySelector(".toast").classList.add("toast--hidden") + } + className="toast-close" + > + + + + ); +}; + +export default ToastSubscribed; diff --git a/src/components/modal/index.js b/src/components/modal/index.js new file mode 100644 index 00000000000..da4b0bb1cc8 --- /dev/null +++ b/src/components/modal/index.js @@ -0,0 +1,61 @@ +import React, { useEffect } from "react"; +import "./styles.css"; + +const Modal = ({ isOpen, onClose, children }) => { + useEffect(() => { + if (isOpen) { + document.body.style.overflow = "hidden"; + } else { + document.body.style.overflow = "unset"; + } + + return () => { + document.body.style.overflow = "unset"; + }; + }, [isOpen]); + + useEffect(() => { + const handleEscape = (event) => { + if (event.key === "Escape" && isOpen) { + onClose(); + } + }; + + document.addEventListener("keydown", handleEscape); + return () => document.removeEventListener("keydown", handleEscape); + }, [isOpen, onClose]); + + if (!isOpen) return null; + + return ( +
+
e.stopPropagation()} + role="dialog" + aria-modal="true" + > + +
{children}
+
+
+ ); +}; + +export default Modal; diff --git a/src/components/modal/styles.css b/src/components/modal/styles.css new file mode 100644 index 00000000000..b01dea531a8 --- /dev/null +++ b/src/components/modal/styles.css @@ -0,0 +1,65 @@ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(38, 35, 47, 0.2); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal-content { + background: #fff; + border-radius: 24px; + padding: 48px; + position: relative; + max-height: 95vh; + overflow-y: auto; + box-shadow: 0 2px 10px rgba(39, 26, 72, 0.18); + animation: modalFadeIn 0.3s ease-out; + width: 100%; + max-width: 600px; +} + +.modal-close { + position: absolute; + top: 44px; + right: 44px; + background: transparent; + border: none; + cursor: pointer; + color: #454348; + padding: 0; + width: 30px; + height: 30px; +} + +.modal-body { + color: #454348; + font-size: 14px; + line-height: 24px; +} + +.modal-body h3 { + margin: 8px 0 24px; +} +.modal-body p { + margin-bottom: 0; +} +.modal-body iframe { + border-radius: 8px; +} + +@keyframes modalFadeIn { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/src/css/custom.css b/src/css/custom.css index 177a281e606..64de7ad430e 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -376,4 +376,48 @@ html[data-theme="dark"] .tabs__item--active { width: 1px; height: 20px; background: #cbd5e1; +} + +.toast { + position: fixed; + bottom: 72px; + right: 26px; + box-shadow: 0px 4px 10px 0px #271A482E; + opacity: 0; + animation: toastFadeIn 0.5s ease; + animation-fill-mode: forwards; + animation-delay: 1s; + transform: translateX(10px); + +} + +.toast--hidden { + animation: toastFadeOut 0.5s ease; + animation-fill-mode: forwards; +} + +.toast-close { + position: absolute; + top: 12px; + right: 16px; + cursor: pointer; +} + + +@keyframes toastFadeIn { + to { + opacity: 1; + transform: translateX(0); + } +} + +@keyframes toastFadeOut { + from { + opacity: 1; + transform: translateX(0); + } + to { + opacity: 0; + transform: translateX(10px); + } } \ No newline at end of file diff --git a/src/theme/BlogListPage/index.js b/src/theme/BlogListPage/index.js index d7309460371..6266def4374 100644 --- a/src/theme/BlogListPage/index.js +++ b/src/theme/BlogListPage/index.js @@ -12,6 +12,7 @@ import SearchMetadata from "@theme/SearchMetadata"; import BlogPostItems from "@theme/BlogPostItems"; import MDXContent from "@theme/MDXContent"; import ComingUp from "../../../changelog/coming-up.mdx"; +import ToastSubscribed from "../../components/ToastSubscribed"; function BlogListPageMetadata(props) { const { metadata } = props; @@ -29,9 +30,11 @@ function BlogListPageMetadata(props) { ); } function BlogListPageContent(props) { - const { metadata, items, sidebar } = props; + const { metadata, items, sidebar, history } = props; + return ( + {history.location.hash === "#subscribed" && } {metadata.page === 1 ? ( diff --git a/src/theme/BlogSidebar/Desktop/index.js b/src/theme/BlogSidebar/Desktop/index.js index 0e580d30f6a..8d2f076528c 100644 --- a/src/theme/BlogSidebar/Desktop/index.js +++ b/src/theme/BlogSidebar/Desktop/index.js @@ -1,11 +1,14 @@ -import React from "react"; +import React, { useState } from "react"; import clsx from "clsx"; import Link from "@docusaurus/Link"; import { translate } from "@docusaurus/Translate"; import { useVisibleBlogSidebarItems } from "@docusaurus/theme-common/internal"; import styles from "./styles.module.css"; +import Modal from "@site/src/components/modal"; export default function BlogSidebarDesktop({ sidebar }) { + const [newsletterModalVisible, setNewsletterModal] = useState(false); const items = useVisibleBlogSidebarItems(sidebar.items); + return ( ); } diff --git a/static/img/icon-check.svg b/static/img/icon-check.svg new file mode 100644 index 00000000000..4bc1ffdaf43 --- /dev/null +++ b/static/img/icon-check.svg @@ -0,0 +1,3 @@ + + +