"use client" import { Link as UILink, NonAnchorLink, classNames, formatHref, isExternalLink, useNavigation, } from "@opensea/ui-kit" import type { Locale, LinkProps as UILinkProps, Url } from "@opensea/ui-kit" import { useLocale } from "@opensea/ui-kit/hooks" import NextLink from "next/link" import { useRouter } from "next/navigation" import React, { createContext, forwardRef, useContext } from "react" type onNavigationCompleted = (startTime: number, duration: number) => unknown export type LinkPrefetch = "onHover" | "inViewport" | false export type LinkProps = Omit & Omit< React.ComponentProps, "href" | "prefetch" | "legacyBehavior" | "locale" > & { href: Url onNavigationCompleted?: onNavigationCompleted prefetch?: LinkPrefetch locale?: Locale } const CONTEXT_VALUE = {} const LinkContext = createContext(undefined) let destination: string | undefined const originalWindowHistoryPushState = typeof window === "undefined" ? () => {} : window.history.pushState function clearPushState() { window.history.pushState = originalWindowHistoryPushState } export const Link = forwardRef(function Link( { href, scroll, shallow, prefetch = "onHover", locale: localeProp, onNavigationCompleted, ...rest }, ref, ) { const { renderLink: NavigationLink } = useNavigation() const localeContext = useLocale() const locale = localeProp ?? localeContext const context = useContext(LinkContext) const router = useRouter() const LinkComponent = NavigationLink ?? NextLink if (isExternalLink(href)) { if (context != undefined) { return } return ( ) } function onPointerEnter( event: React.MouseEvent, ) { if (prefetch === "onHover") { rest.onPointerEnter?.(event as React.PointerEvent) router.prefetch(formatHref(href)) } else { rest.onPointerEnter?.(event as React.PointerEvent) } } function onClick(event: React.MouseEvent) { // We want to open the link in a new tab if the user uses the middle mouse button or the command key. if (event.metaKey || event.ctrlKey || event.button === 1) { event.preventDefault() event.stopPropagation() window.open(formatHref(href), "_blank") return } rest.onClick?.(event) if (onNavigationCompleted) { trackNavigation(formatHref(href), onNavigationCompleted) } } const link = context === undefined ? ( ) : ( // We are rendeing a Link that's nested within another Link. This is not valid HTML (https://www.w3.org/TR/html401/struct/links.html#h-12.2.2), // and can cause rendering issues in React. Instead, we render a span which will behave as a Link (using js APIs) ) return ( {link} ) }) const trackNavigation = ( href: string, onNavigationCompleted: onNavigationCompleted, ) => { if (href === destination) { return } else if (destination !== undefined) { clearPushState() } destination = href const navigationStart = Date.now() type PushStateInput = Parameters window.history.pushState = new Proxy(window.history.pushState, { apply: (target, thisArg, argArray: PushStateInput) => { const duration = Date.now() - navigationStart onNavigationCompleted(navigationStart, duration) destination = undefined clearPushState() return target.apply(thisArg, argArray) }, }) }