my blog https://overreacted.io
at main 1.6 kB view raw
1"use client"; 2 3import { useTransition } from "react"; 4import NextLink from "next/link"; 5import { useRouter } from "next/navigation"; 6import { MouseEvent } from "react"; 7 8function isModifiedEvent(event: MouseEvent<HTMLAnchorElement>) { 9 const eventTarget = event.currentTarget; 10 const target = eventTarget.getAttribute("target"); 11 return ( 12 (target && target !== "_self") || 13 event.metaKey || 14 event.ctrlKey || 15 event.shiftKey || 16 event.altKey || 17 (event.nativeEvent && event.nativeEvent.which === 2) 18 ); 19} 20 21export default function Link({ 22 className, 23 children, 24 style, 25 href, 26 target, 27 ...rest 28}: { 29 className?: string; 30 children?: React.ReactNode; 31 style?: React.CSSProperties; 32 href: string; 33 target?: string; 34} & React.ComponentProps<typeof NextLink>) { 35 const router = useRouter(); 36 const [isNavigating, trackNavigation] = useTransition(); 37 const isExternal = /^https?:\/\//.test(href); 38 if (!target && isExternal) { 39 target = "_blank"; 40 } 41 return ( 42 <NextLink 43 {...rest} 44 target={target} 45 href={href} 46 onClick={(e) => { 47 if (!isModifiedEvent(e)) { 48 e.preventDefault(); 49 trackNavigation(() => { 50 router.push(e.currentTarget.href); 51 }); 52 } 53 }} 54 className={[className, "scale-100 active:scale-100"].join(" ")} 55 style={{ 56 ...style, 57 transform: isNavigating ? "scale(1)" : "", 58 opacity: isNavigating ? 0.85 : 1, 59 transition: "transform 0.2s ease-in-out, opacity 0.2s 0.4s linear", 60 }} 61 > 62 {children} 63 </NextLink> 64 ); 65}