import * as React from "react"; import { useThemeConfig, useThemeHooks } from "@redocly/theme/core/hooks"; import { LanguagePicker } from "@redocly/theme/components/LanguagePicker/LanguagePicker"; import { slugify } from "../../helpers"; import { Link } from "@redocly/theme/components/Link/Link"; import { ColorModeSwitcher } from "@redocly/theme/components/ColorModeSwitcher/ColorModeSwitcher"; import { Search } from "@redocly/theme/components/Search/Search"; import arrowUpRight from "../../../static/img/icons/arrow-up-right-custom.svg"; import moment from "moment-timezone"; // @ts-ignore const alertBanner = { show: false, message: "APEX 2025", button: "REGISTER", link: "https://www.xrpledgerapex.com/?utm_source=xrplwebsite&utm_medium=direct&utm_campaign=xrpl-event-ho-xrplapex-glb-2025-q1_xrplwebsite_ari_arp_bf_rsvp&utm_content=cta_btn_english_pencilbanner" }; export function AlertBanner({ message, button, link, show }) { const { useTranslate } = useThemeHooks(); const { translate } = useTranslate(); const bannerRef = React.useRef(null); const [displayDate, setDisplayDate] = React.useState("JUNE 10-12"); React.useEffect(() => { const calculateCountdown = () => { // Calculate days until June 11, 2025 8AM Singapore time // This will automatically adjust for the user's timezone const target = moment.tz('2025-06-11 08:00:00', 'Asia/Singapore'); const now = moment(); const daysUntil = target.diff(now, 'days'); // Show countdown if event is in the future, otherwise show the provided date let newDisplayDate = "JUNE 10-12"; if (daysUntil > 0) { newDisplayDate = daysUntil === 1 ? 'IN 1 DAY' : `IN ${daysUntil} DAYS`; } else if (daysUntil === 0) { // Check if it's today const hoursUntil = target.diff(now, 'hours'); newDisplayDate = hoursUntil > 0 ? 'TODAY' : "JUNE 10-12"; } setDisplayDate(newDisplayDate); }; // Calculate immediately calculateCountdown(); // Update every hour const interval = setInterval(calculateCountdown, 60 * 60 * 1000); return () => clearInterval(interval); }, []); React.useEffect(() => { const banner = bannerRef.current; if (!banner) return; const handleMouseEnter = () => { banner.classList.add("has-hover"); }; // Attach the event listener banner.addEventListener("mouseenter", handleMouseEnter); // Clean up the event listener on unmount return () => { banner.removeEventListener("mouseenter", handleMouseEnter); }; }, []); if (show) { return (
{translate(message)}
{displayDate}
{translate(button)}
Get Tickets Icon
); } return null; } export function Navbar(props) { // const [isOpen, setIsOpen] = useMobileMenu(false); const themeConfig = useThemeConfig(); const { useL10n } = useThemeHooks(); const { changeLanguage } = useL10n(); const menu = themeConfig.navbar?.items; const logo = themeConfig.logo; const { href, altText, items } = props; const pathPrefix = ""; const navItems = menu.map((item, index) => { if (item.type === "group") { return ( ); } else { return ( {item.label} ); } }); React.useEffect(() => { // Turns out jQuery is necessary for firing events on Bootstrap v4 // dropdowns. These events set classes so that the search bar and other // submenus collapse on mobile when you expand one submenu. const dds = $("#topnav-pages .dropdown"); const top_main_nav = document.querySelector("#top-main-nav"); dds.on("show.bs.dropdown", (evt) => { top_main_nav.classList.add("submenu-expanded"); }); dds.on("hidden.bs.dropdown", (evt) => { top_main_nav.classList.remove("submenu-expanded"); }); // Close navbar on .dropdown-item click const toggleNavbar = () => { const navbarToggler = document.querySelector(".navbar-toggler"); const isNavbarCollapsed = navbarToggler.getAttribute("aria-expanded") === "true"; if (isNavbarCollapsed) { navbarToggler.click(); // Simulate click to toggle navbar } }; const dropdownItems = document.querySelectorAll(".dropdown-item"); dropdownItems.forEach((item) => { item.addEventListener("click", toggleNavbar); }); // Cleanup function to remove event listeners return () => { dropdownItems.forEach((item) => { item.removeEventListener("click", toggleNavbar); }); }; }, []); return ( <> {navItems}
); } export function TopNavCollapsible({ children }) { return (
{children}
); } export function NavDropdown(props) { const { label, items, pathPrefix, labelTranslationKey } = props; const { useTranslate } = useThemeHooks(); const { translate } = useTranslate(); const dropdownGroups = items.map((item, index) => { if (item.items) { const groupLinks = item.items.map((item2, index2) => { const cls2 = item2.external ? "dropdown-item external-link" : "dropdown-item"; let item2_href = item2.link; if (item2_href && !item2_href.match(/^https?:/)) { item2_href = pathPrefix + item2_href; } //conditional specific for brand kit if (item2.link === "/XRPL_Brand_Kit.zip") { return ( {translate(item2.labelTranslationKey, item2.label)} ); } return ( {translate(item2.labelTranslationKey, item2.label)} ); }); const clnm = "navcol col-for-" + slugify(item.label); return (
{translate(item.labelTranslationKey, item.label)}
{groupLinks}
); } else if (item.icon) { const hero_id = "dropdown-hero-for-" + slugify(label); const img_alt = item.label + " icon"; let hero_href = item.link; if (hero_href && !hero_href.match(/^https?:/)) { hero_href = pathPrefix + hero_href; } const splitlabel = item.label.split(" || "); let splittranslationkey = ["", ""]; if (item.labelTranslationKey) { splittranslationkey = item.labelTranslationKey.split(" || "); } const newlabel = translate(splittranslationkey[0], splitlabel[0]); const description = translate(splittranslationkey[1], splitlabel[1]); // splitlabel[1] might be undefined, that's ok return ( {img_alt}

{newlabel}

{description}

); } else { const cls = item.external ? "dropdown-item ungrouped external-link" : "dropdown-item ungrouped"; let item_href = item.link; if (item_href && !item_href.match(/^https?:/)) { item_href = pathPrefix + item_href; } return ( {translate(item.labelTranslationKey, item.label)} ); } }); const toggler_id = "topnav_" + slugify(label); const dd_id = "topnav_dd_" + slugify(label); return (
  • {dropdownGroups}
  • ); } export function NavWrapper(props) { return ( ); } export function NavControls(props) { return ( ); } export function MobileMenuIcon() { return (
    ); } export function GetStartedButton() { const { useTranslate } = useThemeHooks(); const { translate } = useTranslate(); return ( {translate("Get Started")} ); } export function NavItems(props) { return ( ); } export function NavItem(props) { return
  • {props.children}
  • ; } export function LogoBlock(props) { const { to, img, altText } = props; return ( {"XRP ); } export class ThemeToggle extends React.Component { auto_update_theme() { const upc = window.localStorage.getItem("user-prefers-color"); let theme = "dark"; // Default to dark theme if (!upc) { // User hasn't saved a preference specifically for this site; check // the browser-level preferences. if ( window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches ) { theme = "light"; } } else { // Follow user's saved setting. theme = upc == "light" ? "light" : "dark"; } const disable_theme = theme == "dark" ? "light" : "dark"; document.documentElement.classList.add(theme); document.documentElement.classList.remove(disable_theme); } user_choose_theme() { const new_theme = document.documentElement.classList.contains("dark") ? "light" : "dark"; window.localStorage.setItem("user-prefers-color", new_theme); document.body.style.transition = "background-color .2s ease"; const disable_theme = new_theme == "dark" ? "light" : "dark"; document.documentElement.classList.add(new_theme); document.documentElement.classList.remove(disable_theme); } render() { return (
    ); } componentDidMount() { this.auto_update_theme(); } }