- Add Next.js frontend service (nextjs) with Dockerfile and source - Update docker-compose.yml: image names, Drupal 11.3.3, nextjs service - Add docker-compose.override.yml.disabled for dev hot-reload - Add install-headless-modules.sh for OAuth/JSON:API module setup - Add README.md with full setup and configuration guide - Update nginx/Dockerfile and nginx.conf.template for cms. subdomain - Update drupal/Dockerfile PHP-FPM build args - Gitignore **/.vscode/ to prevent IDE workspace files from being tracked
56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useRef, useState, type ReactNode } from "react"
|
|
|
|
interface ScrollRevealSectionProps {
|
|
children: ReactNode
|
|
/** When true, section starts visible (no opacity-20 flash). Use for above-the-fold hero. */
|
|
initialVisible?: boolean
|
|
/** Delay in ms before the reveal animation starts after the section enters view. */
|
|
revealDelayMs?: number
|
|
}
|
|
|
|
export function ScrollRevealSection({
|
|
children,
|
|
initialVisible = false,
|
|
revealDelayMs = 0,
|
|
}: ScrollRevealSectionProps) {
|
|
const ref = useRef<HTMLDivElement>(null)
|
|
const [isVisible, setIsVisible] = useState(initialVisible)
|
|
|
|
useEffect(() => {
|
|
const el = ref.current
|
|
if (!el) return
|
|
|
|
let timeoutId: ReturnType<typeof setTimeout> | null = null
|
|
|
|
const observer = new IntersectionObserver(
|
|
([entry]) => {
|
|
if (!entry.isIntersecting) return
|
|
if (revealDelayMs <= 0) {
|
|
setIsVisible(true)
|
|
return
|
|
}
|
|
timeoutId = setTimeout(() => setIsVisible(true), revealDelayMs)
|
|
},
|
|
{ threshold: 0.1, rootMargin: "0px 0px -80px 0px" }
|
|
)
|
|
|
|
observer.observe(el)
|
|
return () => {
|
|
if (timeoutId) clearTimeout(timeoutId)
|
|
observer.disconnect()
|
|
}
|
|
}, [revealDelayMs])
|
|
|
|
return (
|
|
<div
|
|
ref={ref}
|
|
className={`transition-all duration-700 ease-out ${
|
|
isVisible ? "translate-y-0 opacity-100" : "translate-y-12 opacity-20"
|
|
}`}
|
|
>
|
|
{children}
|
|
</div>
|
|
)
|
|
}
|