"use client"; import { CheckIcon, HeartIcon, MessageCircleIcon, Repeat2Icon, ShareIcon } from "lucide-react"; import { AnimatePresence, motion, useReducedMotion } from "motion/react"; import { type ReactNode, useEffect, useRef, useState } from "react"; import { cn } from "@/lib/utils"; const compact = new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 }); const full = new Intl.NumberFormat("en-US"); const format = (n: number) => (n < 10_000 ? full.format(n) : compact.format(n)); type Slot = { char: string; id: string }; function toSlots(value: number, previous: Slot[], version: number): Slot[] { return [...format(value)].reverse().map((char, i) => (previous[i]?.char === char ? previous[i] : { char, id: `${char}:${version}` })); } export function RollingCount({ value, className }: { value: number; className?: string }) { const [state, setState] = useState(() => ({ value, version: 0, direction: 1, slots: toSlots(value, [], 0) })); if (value !== state.value) { const version = state.version + 1; setState({ value, version, direction: value > state.value ? 1 : -1, slots: toSlots(value, state.slots, version) }); } const reduce = useReducedMotion(); const { direction } = state; const slots = [...state.slots].reverse(); return ( {value} {slots.map((slot, i) => { const fromRight = slots.length - i; return ( {slot.char} ); })} ); } function Burst({ trigger, className }: { trigger: number; className: string }) { const reduce = useReducedMotion(); if (reduce || trigger === 0) return null; return ( {Array.from({ length: 7 }, (_, i) => { const angle = (i / 7) * Math.PI * 2 - Math.PI / 2; return ( ); })} ); } interface ActionProps { label: string; icon: ReactNode; count?: number; active?: boolean; activeClass: string; onClick: () => void; pop: number; burst?: boolean; toggle?: boolean; } function Action({ label, icon, count, active, activeClass, onClick, pop, burst, toggle }: ActionProps) { const reduce = useReducedMotion(); return ( ); } export interface SocialPillProps { likes?: number; comments?: number; reposts?: number; liked?: boolean; reposted?: boolean; onLikeChange?: (liked: boolean) => void; onRepostChange?: (reposted: boolean) => void; onComment?: () => void; onShare?: () => void; className?: string; } export function SocialPill({ likes = 0, comments = 0, reposts = 0, liked: likedProp = false, reposted: repostedProp = false, onLikeChange, onRepostChange, onComment, onShare, className, }: SocialPillProps) { const [liked, setLiked] = useState(likedProp); const [reposted, setReposted] = useState(repostedProp); const [pops, setPops] = useState({ like: 0, comment: 0, repost: 0, share: 0 }); const [copied, setCopied] = useState(false); const timer = useRef(undefined); useEffect(() => () => clearTimeout(timer.current), []); const bump = (key: keyof typeof pops) => setPops((p) => ({ ...p, [key]: p[key] + 1 })); return (
} label={liked ? "Unlike" : "Like"} onClick={() => { bump("like"); setLiked(!liked); onLikeChange?.(!liked); }} pop={pops.like} toggle /> } label="Comment" onClick={() => { bump("comment"); onComment?.(); }} pop={pops.comment} /> } label={reposted ? "Undo repost" : "Repost"} onClick={() => { bump("repost"); setReposted(!reposted); onRepostChange?.(!reposted); }} pop={pops.repost} toggle /> {copied ? : } } label={copied ? "Link copied" : "Share"} onClick={() => { bump("share"); onShare?.(); setCopied(true); clearTimeout(timer.current); timer.current = window.setTimeout(() => setCopied(false), 1600); }} pop={pops.share} />
); }