nsfwapp/frontend/src/components/ui/LazyMount.tsx
2026-01-13 14:00:05 +01:00

56 lines
1.3 KiB
TypeScript

'use client'
import * as React from 'react'
type Props = {
children: React.ReactNode
/** Wenn true: sofort mounten (ohne IntersectionObserver) */
force?: boolean
/** Vorladen bevor es wirklich sichtbar ist */
rootMargin?: string
/** Optional: Platzhalter bis mounted */
placeholder?: React.ReactNode
className?: string
}
export default function LazyMount({
children,
force = false,
rootMargin = '300px',
placeholder = null,
className,
}: Props) {
const ref = React.useRef<HTMLDivElement | null>(null)
const [mounted, setMounted] = React.useState<boolean>(force)
// Wenn force später true wird (z.B. inlinePlay startet) -> sofort mounten
React.useEffect(() => {
if (force && !mounted) setMounted(true)
}, [force, mounted])
React.useEffect(() => {
if (mounted || force) return
const el = ref.current
if (!el) return
const io = new IntersectionObserver(
(entries) => {
if (entries.some((e) => e.isIntersecting)) {
setMounted(true)
io.disconnect()
}
},
{ rootMargin }
)
io.observe(el)
return () => io.disconnect()
}, [mounted, force, rootMargin])
return (
<div ref={ref} className={className}>
{mounted ? children : placeholder}
</div>
)
}