diff --git a/packages/docs-gesture-handler/src/components/TopPromoRotator/index.tsx b/packages/docs-gesture-handler/src/components/TopPromoRotator/index.tsx index faf7ac203d..c4a9fb0a7b 100644 --- a/packages/docs-gesture-handler/src/components/TopPromoRotator/index.tsx +++ b/packages/docs-gesture-handler/src/components/TopPromoRotator/index.tsx @@ -1,9 +1,15 @@ import clsx from 'clsx'; -import React, { type ReactNode, useEffect, useMemo, useState } from 'react'; +import { type ReactNode, useEffect, useMemo, useState } from 'react'; import HandIcon from '../HandIcon'; import styles from './styles.module.css'; +declare global { + interface Window { + dataLayer?: unknown[]; + } +} + type Promo = { key: string; href: string; @@ -45,11 +51,44 @@ const PROMOS: readonly Promo[] = [ }, ]; -export default function TopPromoRotator() { +// bump when adding promos so users who dismissed banner see the new one +export const PROMO_VERSION = 1; + +type Props = { + onClose?: () => void; +}; + +export default function TopPromoRotator({ onClose }: Props) { const promos = useMemo(() => PROMOS, []); const [index, setIndex] = useState(0); + useEffect(() => { + if (typeof window === 'undefined' || typeof document === 'undefined') { + return; + } + + const existingScript = document.querySelector( + 'script[src*="www.googletagmanager.com/gtm.js?id=GTM-WV2G3SQL"]' + ); + + if (existingScript) return; + + (function (w: Window, d: Document, s: string, l: string, i: string) { + w.dataLayer = w.dataLayer || []; + w.dataLayer.push({ + 'gtm.start': new Date().getTime(), + event: 'gtm.js', + }); + const f = d.getElementsByTagName(s)[0] as HTMLScriptElement; + const j = d.createElement(s) as HTMLScriptElement; + const dl = l !== 'dataLayer' ? `&l=${l}` : ''; + j.async = true; + j.src = `https://www.googletagmanager.com/gtm.js?id=${i}${dl}`; + f.parentNode?.insertBefore(j, f); + })(window, document, 'script', 'dataLayer', 'GTM-WV2G3SQL'); + }, []); + useEffect(() => { const id = window.setInterval(() => { setIndex(i => (i + 1) % promos.length); @@ -89,6 +128,15 @@ export default function TopPromoRotator() { ))} + {onClose && ( + + )} {typeof active.label === 'string' ? active.label : ''} diff --git a/packages/docs-gesture-handler/src/components/TopPromoRotator/styles.module.css b/packages/docs-gesture-handler/src/components/TopPromoRotator/styles.module.css index 78a5c2c43d..279b450b83 100644 --- a/packages/docs-gesture-handler/src/components/TopPromoRotator/styles.module.css +++ b/packages/docs-gesture-handler/src/components/TopPromoRotator/styles.module.css @@ -75,3 +75,17 @@ text-decoration: underline; text-underline-offset: 2px; } + +.closeButton { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + border: none; + background: transparent; + color: #001a72; + cursor: pointer; + padding: 4px; + line-height: 1; + font-size: 16px; +} diff --git a/packages/docs-gesture-handler/src/theme/Navbar/index.js b/packages/docs-gesture-handler/src/theme/Navbar/index.js index 19483b5ab7..03941377a7 100644 --- a/packages/docs-gesture-handler/src/theme/Navbar/index.js +++ b/packages/docs-gesture-handler/src/theme/Navbar/index.js @@ -1,9 +1,48 @@ import React from 'react'; import useBaseUrl from '@docusaurus/useBaseUrl'; +import { useLocation } from '@docusaurus/router'; import { Navbar } from '@swmansion/t-rex-ui'; -import TopPromoRotator from '@site/src/components/TopPromoRotator'; +import TopPromoRotator, { + PROMO_VERSION, +} from '@site/src/components/TopPromoRotator'; export default function NavbarWrapper(props) { + const location = useLocation(); + const baseUrl = useBaseUrl('/'); + const isLanding = location.pathname === baseUrl; + + const [showPromo, setShowPromo] = React.useState(true); + + React.useEffect(() => { + if (isLanding || typeof globalThis === 'undefined') { + return; + } + + try { + const raw = globalThis.localStorage?.getItem('topPromoState'); + const state = raw ? JSON.parse(raw) : null; + if (state?.v === PROMO_VERSION && state?.hidden) { + setShowPromo(false); + } + } catch (_) { + // ignore + } + }, [isLanding]); + + const handleClosePromo = React.useCallback(() => { + setShowPromo(false); + if (typeof globalThis !== 'undefined') { + try { + globalThis.localStorage?.setItem( + 'topPromoState', + JSON.stringify({ v: PROMO_VERSION, hidden: true }) + ); + } catch { + // ignore + } + } + }, []); + const titleImages = { light: useBaseUrl('/img/title.svg'), dark: useBaseUrl('/img/title-dark.svg'), @@ -12,9 +51,14 @@ export default function NavbarWrapper(props) { const heroImages = { logo: useBaseUrl('/img/logo-hero.svg'), }; + return (
- + {isLanding ? ( + + ) : ( + showPromo && + )}
);