From 1db797b4db7d708da7316934a129547da79bceaf Mon Sep 17 00:00:00 2001 From: GROK Date: Thu, 20 Aug 2026 16:59:19 -0500 Subject: [PATCH] Add immediate opt-out so FadeIn and AnimatedNumber can run on mount Fixes #1. Dashboards and wall displays never scroll, so whileInView left FadeIn at opacity 0 and AnimatedNumber stuck at 0. Default is unchanged. --- src/animated/animated-number.tsx | 8 ++++++-- src/components/stat-card.tsx | 5 ++++- src/motion/index.tsx | 9 +++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/animated/animated-number.tsx b/src/animated/animated-number.tsx index 8168aed..3ae00a5 100755 --- a/src/animated/animated-number.tsx +++ b/src/animated/animated-number.tsx @@ -8,10 +8,13 @@ export function AnimatedNumber({ value, format = defaultFormat, className, + immediate = false, }: { value: number; format?: (n: number) => string; className?: string; + /** Count up on mount instead of waiting to scroll into view. Dashboards. */ + immediate?: boolean; }) { const ref = React.useRef(null); const inView = useInView(ref, { once: true, margin: "-20% 0px" }); @@ -19,11 +22,12 @@ export function AnimatedNumber({ const mv = useMotionValue(0); const spring = useSpring(mv, { stiffness: 90, damping: 20 }); const [display, setDisplay] = React.useState(format(0)); + const active = immediate || inView; React.useEffect(() => { if (reduce) { setDisplay(format(value)); return; } - if (inView) mv.set(value); - }, [inView, value, reduce, mv, format]); + if (active) mv.set(value); + }, [active, value, reduce, mv, format]); React.useEffect(() => spring.on("change", (v) => setDisplay(format(v))), [spring, format]); diff --git a/src/components/stat-card.tsx b/src/components/stat-card.tsx index b1b4c2a..c785582 100755 --- a/src/components/stat-card.tsx +++ b/src/components/stat-card.tsx @@ -24,6 +24,8 @@ export interface StatCardProps { intent?: StatIntent; icon?: React.ReactNode; className?: string; + /** Count up on mount. Pass through to AnimatedNumber. */ + immediate?: boolean; } /** Dashboard metric tile. Pass `animate` for a count-up, or `value` for static. */ @@ -36,6 +38,7 @@ export function StatCard({ intent = "default", icon, className, + immediate, }: StatCardProps) { return (
@@ -44,7 +47,7 @@ export function StatCard({ {icon && {icon}}

- {animate != null ? : value} + {animate != null ? : value}

{sub &&

{sub}

} diff --git a/src/motion/index.tsx b/src/motion/index.tsx index 67f68a5..43c701d 100755 --- a/src/motion/index.tsx +++ b/src/motion/index.tsx @@ -9,19 +9,24 @@ export function FadeIn({ delay = 0, y = 8, className, + immediate = false, }: { children: React.ReactNode; delay?: number; y?: number; className?: string; + /** Animate on mount instead of waiting to scroll into view. Dashboards. */ + immediate?: boolean; }) { const reduce = useReducedMotion(); + const visible = { opacity: 1, y: 0 }; return ( {children}