Skip to content

Create CarbonAds component and update the placement #1166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions src/components/CarbonAds.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"use client"
import { useEffect, useRef, useState, useCallback } from "react"
import { usePathname } from "next/navigation"

const CARBON_SCRIPT_ID = "_carbonads_js"
const CARBON_SCRIPT_SRC =
"https://cdn.carbonads.com/carbon.js?serve=CW7DTKQ7&placement=react-hook-formcom&format=cover"

// Persist mount state across SPA navigation (module-level, not per instance)
let hasMounted = false

export default function CarbonAds() {
// --- Refs and State ---
const containerRef = useRef<HTMLDivElement>(null)
const debounceRef = useRef<NodeJS.Timeout | null>(null)
const [fade, setFade] = useState(1)
const [height, setHeight] = useState<number | null>(null)
const pathname = usePathname()

/**
* Debounced reload of Carbon Ads script.
* Removes existing ad/script, injects new script, and sets height after load.
* Debounce prevents rapid reloads on fast route changes.
*/
const reloadCarbonAds = useCallback(() => {
if (debounceRef.current) clearTimeout(debounceRef.current)
debounceRef.current = setTimeout(() => {
// Remove existing ad and script if present
const carbon = containerRef.current?.querySelector("#carbonads")
if (carbon) carbon.remove()
const script = document.getElementById(CARBON_SCRIPT_ID)
if (script) script.remove()

// Inject new Carbon Ads script
const newScript = document.createElement("script")
newScript.id = CARBON_SCRIPT_ID
newScript.async = true
newScript.src = CARBON_SCRIPT_SRC
newScript.type = "text/javascript"
if (containerRef.current) {
containerRef.current.appendChild(newScript)
}

// Try to set height after ad loads (poll for up to 2s)
let heightSet = false
const trySetHeight = () => {
const carbon = containerRef.current?.querySelector(
"#carbonads"
) as HTMLElement | null
if (carbon && carbon.offsetHeight > 0) {
setHeight(carbon.offsetHeight)
heightSet = true
}
}
let tries = 0
const interval = setInterval(() => {
trySetHeight()
tries++
if (heightSet || tries > 10) clearInterval(interval)
}, 200)
}, 100)
}, [])

// --- Initial Mount: Only load ad once ---
useEffect(() => {
// Only run on very first mount (SPA safe)
if (!hasMounted) {
reloadCarbonAds()
hasMounted = true
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

// --- Path Change: Reload ad, skip first render ---
useEffect(() => {
// Only reload on subsequent path changes
if (hasMounted) {
reloadCarbonAds()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname])

/**
* On scroll, fade and shrink the ad container
* Uses the actual #carbonads height as basis for smoothness.
*/
useEffect(() => {
function handleScroll() {
const maxScroll = 200 // px after which it's fully hidden
const scrollY = window.scrollY
const ratio = Math.max(0, 1 - scrollY / maxScroll)
setFade(ratio)
const carbon = containerRef.current?.querySelector(
"#carbonads"
) as HTMLElement | null
const baseHeight = carbon ? carbon.offsetHeight : (height ?? 120)
setHeight(Math.max(0, ratio * baseHeight))
}
window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll)
}, [height])

// --- Render ---
return (
<div
id="carbonAdsContainer"
className="carbonAdsContainer"
ref={containerRef}
style={{
opacity: fade,
height: height !== null ? `${height}px` : undefined,
transition:
"opacity 0.4s cubic-bezier(.4,0,.2,1), height 0.6s cubic-bezier(.4,0,.2,1)",
overflow: "hidden",
willChange: "opacity, height",
}}
/>
)
}
4 changes: 2 additions & 2 deletions src/components/Menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styles from "./SideMenu.module.css"
import typographyStyles from "../../styles/typography.module.css"
import { useRouter } from "next/router"
import { Pages } from "../../types/types"
import CarbonAds from "../CarbonAds"

function Menu({ pages = [] }: { pages: Pages }) {
const router = useRouter()
Expand All @@ -13,6 +14,7 @@ function Menu({ pages = [] }: { pages: Pages }) {
<aside className={styles.menu}>
<div>
<div className={styles.titleList}>
<CarbonAds />
<h4
className={typographyStyles.title}
style={{
Expand Down Expand Up @@ -70,8 +72,6 @@ function Menu({ pages = [] }: { pages: Pages }) {
)
})}
</ul>

<div id="carbon-cover" />
</div>
</aside>
)
Expand Down
8 changes: 4 additions & 4 deletions src/components/layout.css
Original file line number Diff line number Diff line change
Expand Up @@ -946,10 +946,10 @@ pre[class*="language-"] {
}
}

#carbon-responsive {
margin: 0 auto 50px;
.carbonAdsContainer {
margin-block-end: 2em;
}

#carbon-cover {
margin: 0 auto 50px;
#carbon-responsive .carbon-poweredby {
color: inherit;
}
6 changes: 0 additions & 6 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ export default function Document() {
strategy="afterInteractive"
src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"
/>
<Script
async
strategy="afterInteractive"
src="https://cdn.carbonads.com/carbon.js?serve=CW7DTKQ7&placement=react-hook-formcom&format=cover"
id="_carbonads_js"
/>
<link
rel="shortcut icon"
href="/images/logo/react-hook-form-logo-only.png"
Expand Down