diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js index de52af0eb..a9a26cda3 100644 --- a/docs/docusaurus.config.js +++ b/docs/docusaurus.config.js @@ -1,9 +1,28 @@ // @ts-check // Note: type annotations allow type checking and IDEs autocompletion +import { topbarBannerReservationScript } from '@swmansion/t-rex-ui/topbar-banner'; +import { TOP_BAR_BANNER } from './src/components/topbarBanner.config.ts'; + const lightCodeTheme = require('./src/theme/CodeBlock/highlighting-light.js'); const darkCodeTheme = require('./src/theme/CodeBlock/highlighting-dark.js'); + +const firstBannerZone = TOP_BAR_BANNER.zones[0]; +const bannerReservationHeadTags = firstBannerZone + ? [ + { + tagName: 'script', + attributes: { type: 'text/javascript' }, + innerHTML: topbarBannerReservationScript( + firstBannerZone.zoneId, + firstBannerZone.contentId, + TOP_BAR_BANNER.hiddenPaths, + ), + }, + ] + : []; + /** @type {import('@docusaurus/types').Config} */ const config = { title: 'React Native Enriched HTML', @@ -64,6 +83,10 @@ const config = { require.resolve('@swmansion/t-rex-ui/preset'), ], + headTags: bannerReservationHeadTags, + + clientModules: [require.resolve('./src/clientModules/topbarBannerRefresh.ts')], + plugins: [ function transpileTRexUiTheme() { return { @@ -84,13 +107,12 @@ const config = { }, }; }, - // TODO: enable Google Tag Manager with a container id for this site. - // process.env.NODE_ENV === 'production' && [ - // '@docusaurus/plugin-google-tag-manager', - // { - // containerId: 'GTM-XXXXXXX', - // }, - // ], + process.env.NODE_ENV === 'production' && [ + '@docusaurus/plugin-google-tag-manager', + { + containerId: 'GTM-N5QK8TMT', + }, + ], ].filter(Boolean), themeConfig: diff --git a/docs/package.json b/docs/package.json index cf77fce5c..199d0b354 100644 --- a/docs/package.json +++ b/docs/package.json @@ -27,7 +27,7 @@ "@emotion/styled": "^11.14.0", "@mdx-js/react": "^3.0.0", "@mui/material": "^7.1.0", - "@swmansion/t-rex-ui": "1.3.2", + "@swmansion/t-rex-ui": "1.3.4", "clsx": "^2.1.0", "copy-text-to-clipboard": "3.2.2", "prettier": "^3.6.2", diff --git a/docs/src/clientModules/topbarBannerRefresh.ts b/docs/src/clientModules/topbarBannerRefresh.ts new file mode 100644 index 000000000..dee76d4de --- /dev/null +++ b/docs/src/clientModules/topbarBannerRefresh.ts @@ -0,0 +1,25 @@ +import type { ClientModule } from '@docusaurus/types'; +import type { BannerZone } from '@swmansion/t-rex-ui'; +import { TOP_BAR_BANNER } from '../components/topbarBanner.config'; + +// Revive's async tag only scans slots once, on initial load; re-trigger it after each SPA nav so remounted slots get filled. +const contentIds = Array.from( + new Set(TOP_BAR_BANNER.zones.map((zone: BannerZone) => zone.contentId)), +); + +function refreshBannerContent() { + contentIds.forEach((contentId) => { + document.dispatchEvent(new CustomEvent(`content-${contentId}-refresh`)); + }); +} + +export const onRouteDidUpdate: ClientModule['onRouteDidUpdate'] = ({ + previousLocation, + location, +}) => { + if (!previousLocation || previousLocation.pathname === location.pathname) { + return; + } + // Defer until after the new route's nodes are actually in the DOM. + setTimeout(refreshBannerContent, 0); +}; \ No newline at end of file diff --git a/docs/src/components/TopPromoRotator/index.tsx b/docs/src/components/TopPromoRotator/index.tsx deleted file mode 100644 index 6ff88fad6..000000000 --- a/docs/src/components/TopPromoRotator/index.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import clsx from 'clsx'; -import React, { 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; - bg: string; - buttonLabel: string; - label: ReactNode; -}; - -const PROMOS: readonly Promo[] = [ - { - key: 'appjs', - href: 'https://appjs.co?origin=swmansion_bar', - bg: '#C7CEF5', - buttonLabel: 'Get your tickets', - label: ( - <> - App.js Conf 2026 - - {' '} - is just around the corner! - - - ), - }, -]; - -export default function TopPromoRotator() { - 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); - }, 5_000); - - return () => window.clearInterval(id); - }, [promos.length]); - - const active = promos[index]; - - const barHeight = 50; - const translateY = `translateY(-${index * barHeight}px)`; - - return ( -
-
- {promos.map(p => ( - - {p.label} - - ))} -
- - {typeof active.label === 'string' ? active.label : ''} - -
- ); -} diff --git a/docs/src/components/TopPromoRotator/styles.module.css b/docs/src/components/TopPromoRotator/styles.module.css deleted file mode 100644 index 78a5c2c43..000000000 --- a/docs/src/components/TopPromoRotator/styles.module.css +++ /dev/null @@ -1,77 +0,0 @@ -.wrapper { - position: relative; - height: 50px; - min-height: 50px; - max-height: 50px; - width: 100%; - overflow: hidden; - display: flex; - align-items: center; - justify-content: center; - font-size: 1rem; - font-weight: 500; -} - -.slider { - position: absolute; - inset: 0; - will-change: transform; -} - -.banner { - display: flex; - height: 50px; - min-height: 50px; - width: 100%; - align-items: center; - justify-content: center; - gap: 0.5rem; - padding: 0 0.75rem; - font-size: 1rem; - font-weight: 500; - line-height: 1; - color: #001a72; - text-align: center; - text-decoration: none; - transition: background-color 300ms ease-out; - white-space: nowrap; - box-sizing: border-box; -} - -.banner:hover { - color: #001a72 !important; - text-decoration: none !important; - background-color: rgba(0, 0, 0, 0.05); -} - -.banner:hover * { - text-decoration: none !important; -} - -.banner:hover .underline { - text-decoration: underline !important; - text-underline-offset: 2px; -} - -.hiddenOnMobile { - display: inline; -} - -@media (max-width: 768px) { - .hiddenOnMobile { - display: none; - } -} - -.icon { - flex-shrink: 0; - width: 28px; - height: 28px; - transform: rotate(-90deg); - display: block; -} - -.underline { - text-decoration: underline; - text-underline-offset: 2px; -} diff --git a/docs/src/components/topbarBanner.config.ts b/docs/src/components/topbarBanner.config.ts new file mode 100644 index 000000000..294b24cfd --- /dev/null +++ b/docs/src/components/topbarBanner.config.ts @@ -0,0 +1,21 @@ + +export const TOP_BAR_BANNER = { + rotateIntervalMs: 4000, + zones: [ + { + zoneId: 'enriched-topbar-1', + contentId: 'ea15c4216158c4097b65fe6504a4b3b7', + fallbackBgColor: '#001a72', + }, + { + zoneId: 'enriched-topbar-2', + contentId: 'ea15c4216158c4097b65fe6504a4b3b7', + fallbackBgColor: '#001a72', + }, + { + zoneId: 'enriched-topbar-3', + contentId: 'ea15c4216158c4097b65fe6504a4b3b7', + fallbackBgColor: '#001a72', + }, + ] satisfies BannerZone[], +}; \ No newline at end of file diff --git a/docs/src/theme/Navbar/index.js b/docs/src/theme/Navbar/index.js index c1c85aed5..720435414 100644 --- a/docs/src/theme/Navbar/index.js +++ b/docs/src/theme/Navbar/index.js @@ -1,11 +1,22 @@ import React from 'react'; -import { Navbar } from '@swmansion/t-rex-ui'; -import TopPromoRotator from '@site/src/components/TopPromoRotator'; +import { useLocation } from '@docusaurus/router'; +import { Navbar, TopbarBanner, isBannerHidden } from '@swmansion/t-rex-ui'; +import { TOP_BAR_BANNER } from '@site/src/components/topbarBanner.config'; export default function NavbarWrapper(props) { + const location = useLocation(); + const bannerHidden = isBannerHidden( + location.pathname, + TOP_BAR_BANNER.hiddenPaths, + ); return (
- + {!bannerHidden && ( + + )} =5.0.0" copy-text-to-clipboard: ^3.2.2 + prism-react-renderer: ^2.0.0 react: "*" react-dom: "*" react-icons: "*" - checksum: 10c0/8e88ca4efbc892b01cea5e07f6b46332f32c04008685c7083f52a8f9b0c269f74e6afa2ba5458aae106fcd1ea746a22176f8ec13f5e3823d5405e651e94438ec + checksum: 10c0/64adad0bcce8856d1855e2da153af9429c8a6ae759fb510933163e523db83a70eeceded9d8e50d108f269f7129f143b5f401509aa3ab0331f5fd14eea3295d66 languageName: node linkType: hard @@ -6976,7 +6977,7 @@ __metadata: "@emotion/styled": "npm:^11.14.0" "@mdx-js/react": "npm:^3.0.0" "@mui/material": "npm:^7.1.0" - "@swmansion/t-rex-ui": "npm:1.3.2" + "@swmansion/t-rex-ui": "npm:1.3.4" clsx: "npm:^2.1.0" copy-text-to-clipboard: "npm:3.2.2" prettier: "npm:^3.6.2"