"use client"; import type { HTMLMotionProps } from "motion/react"; import { motion } from "motion/react"; import * as React from "react"; import type { SlidingNumberProps } from "@/components/animate-ui/primitives/texts/sliding-number"; import type { ParticlesEffectProps } from "@/components/animate-ui/primitives/effects/particles"; import type { WithAsChild } from "@/components/animate-ui/primitives/animate/slot"; import type { UseIsInViewOptions } from "@/hooks/use-is-in-view"; import { Particles, ParticlesEffect } from "@/components/animate-ui/primitives/effects/particles"; import { SlidingNumber } from "@/components/animate-ui/primitives/texts/sliding-number"; import { Slot } from "@/components/animate-ui/primitives/animate/slot"; import { getStrictContext } from "@/lib/get-strict-context"; import { useIsInView } from "@/hooks/use-is-in-view"; import { cn } from "@/lib/utils"; type GithubStarsContextType = { stars: number; setStars: (stars: number) => void; currentStars: number; setCurrentStars: (stars: number) => void; isCompleted: boolean; isLoading: boolean; }; const [GithubStarsProvider, useGithubStars] = getStrictContext("GithubStarsContext"); type GithubStarsProps = WithAsChild< { children: React.ReactNode; username?: string; repo?: string; value?: number; delay?: number; } & UseIsInViewOptions & HTMLMotionProps<"div"> >; function GithubStars({ ref, children, username, repo, value, delay = 0, inView = false, inViewMargin = "0px", inViewOnce = true, asChild = false, ...props }: GithubStarsProps) { const { ref: localRef, isInView } = useIsInView(ref as React.Ref, { inView, inViewOnce, inViewMargin, }); const [stars, setStars] = React.useState(value ?? 0); const [currentStars, setCurrentStars] = React.useState(0); const [isLoading, setIsLoading] = React.useState(true); const isCompleted = React.useMemo(() => currentStars === stars, [currentStars, stars]); const Component = asChild ? Slot : motion.div; React.useEffect(() => { if (value !== undefined && username && repo) return; if (!isInView) { setStars(0); setIsLoading(true); return; } const timeout = setTimeout(() => { fetch(`https://api.github.com/repos/${username}/${repo}`) .then(response => response.json()) .then((data) => { if (data && typeof data.stargazers_count === "number") { setStars(data.stargazers_count); } }) .catch(console.error) .finally(() => setIsLoading(false)); }, delay); return () => clearTimeout(timeout); }, [username, repo, value, isInView, delay]); return ( {!isLoading && ( {children} )} ); } type GithubStarsNumberProps = Omit; function GithubStarsNumber({ padStart = true, ...props }: GithubStarsNumberProps) { const { stars, setCurrentStars } = useGithubStars(); return ( ); } type GithubStarsIconProps = { icon: React.ReactElement; color?: string; activeClassName?: string; } & React.ComponentProps; function GithubStarsIcon({ icon: Icon, color = "currentColor", activeClassName, className, ...props }: GithubStarsIconProps) { const { stars, currentStars, isCompleted } = useGithubStars(); const fillPercentage = (currentStars / stars) * 100; return (
); } type GithubStarsParticlesProps = ParticlesEffectProps & { children: React.ReactElement; size?: number; }; function GithubStarsParticles({ children, size = 4, style, ...props }: GithubStarsParticlesProps) { const { isCompleted } = useGithubStars(); return ( {children} ); } type GithubStarsLogoProps = React.SVGProps; function GithubStarsLogo(props: GithubStarsLogoProps) { return ( ); } export { GithubStars, type GithubStarsContextType, GithubStarsIcon, type GithubStarsIconProps, GithubStarsLogo, type GithubStarsLogoProps, GithubStarsNumber, type GithubStarsNumberProps, GithubStarsParticles, type GithubStarsParticlesProps, type GithubStarsProps, useGithubStars, };