refactor(motion): extraer useEnVista, compartido por las ilustraciones

This commit is contained in:
erikgh committed 2026-07-31 10:58:22 -06:00
1 parent fcd58fe00d
commit 1b1b39c67f
4 files changed
+59 -45

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
import { render } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { useEnVista } from '@/lib/motion';
function Sonda() {
const { ref, enVista, reduced } = useEnVista();
return <svg ref={ref} data-en-vista={String(enVista)} data-reducido={String(reduced)} />;
}
describe('useEnVista', () => {
it('hands back a ref to attach, plus the two flags a drawing needs', () => {
const { container } = render(<Sonda />);
const svg = container.querySelector('svg');
expect(svg).toHaveAttribute('data-en-vista');
expect(svg).toHaveAttribute('data-reducido');
});
it('starts out not-yet-in-view so drawings do not skip their entrance', () => {
const { container } = render(<Sonda />);
// jsdom's IntersectionObserver stub never fires, so this also pins the
// safe default: nothing claims to be visible until the observer says so.
expect(container.querySelector('svg')).toHaveAttribute('data-en-vista', 'false');
});
});
+29 -1
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import type { Variants } from 'framer-motion';
// EaseOutExpo — smooth editorial deceleration
@@ -30,6 +30,34 @@ export function usePrefersReducedMotion() {
return reduced;
}
// Shared "draw once when scrolled into view" wiring for the SVG illustrations.
// Returns the ref to attach, whether the element has been seen, and the
// reduced-motion flag the caller needs anyway to pick its render branch.
// Observes nothing under reduced motion — those branches render statically.
export function useEnVista(umbral = 0.4) {
const reduced = usePrefersReducedMotion();
const ref = useRef<SVGSVGElement>(null);
const [enVista, setEnVista] = useState(false);
useEffect(() => {
const el = ref.current;
if (!el || reduced) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setEnVista(true);
observer.unobserve(el);
}
},
{ threshold: umbral },
);
observer.observe(el);
return () => observer.disconnect();
}, [reduced, umbral]);
return { ref, enVista, reduced };
}
// Pass to <motion.*> as motion-reduced guard. When reduced, variants resolve to visible immediately.
export const motionProps = (reduced: boolean) =>
reduced