This changes the behavior of Link prop `keepSearchParams`. Previously it got implicitly merged. Now, any search params in the given `href` prop to Link will override any current search params available on the current page. Handle the merging use case (if there is any) in the components that passes the href to render Link.
127 lines
2.8 KiB
TypeScript
127 lines
2.8 KiB
TypeScript
"use client"
|
|
import NextLink from "next/link"
|
|
import { usePathname, useSearchParams } from "next/navigation"
|
|
import { useCallback, useMemo } from "react"
|
|
|
|
import { useCheckIfExternalLink } from "@/hooks/useCheckIfExternalLink"
|
|
import { trackClick } from "@/utils/tracking"
|
|
|
|
import { linkVariants } from "./variants"
|
|
|
|
import type { LinkProps } from "./link"
|
|
|
|
export default function Link({
|
|
active,
|
|
className,
|
|
color,
|
|
href,
|
|
partialMatch = false,
|
|
textDecoration,
|
|
size,
|
|
scroll = true,
|
|
prefetch,
|
|
variant,
|
|
weight,
|
|
trackingId,
|
|
trackingParams,
|
|
onClick,
|
|
/**
|
|
* Decides if the link should include the current search params in the URL.
|
|
* If the given href also contains search params, they take precedence and
|
|
* override any current search params. If you need to merge them, handle that
|
|
* in your component that passes the href here.
|
|
*/
|
|
keepSearchParams,
|
|
appendToCurrentPath,
|
|
...props
|
|
}: LinkProps) {
|
|
const currentPageSlug = usePathname()
|
|
const searchParams = useSearchParams()
|
|
let isActive = active || currentPageSlug === href
|
|
|
|
if (partialMatch && !isActive) {
|
|
isActive = currentPageSlug === href
|
|
}
|
|
|
|
const classNames = linkVariants({
|
|
active: isActive,
|
|
className,
|
|
textDecoration,
|
|
color,
|
|
size,
|
|
weight,
|
|
variant,
|
|
})
|
|
|
|
const fullUrl = useMemo(() => {
|
|
let newPath = href
|
|
if (appendToCurrentPath) {
|
|
newPath = `${currentPageSlug}${newPath}`
|
|
}
|
|
|
|
if (keepSearchParams && searchParams.size) {
|
|
if (newPath.includes("?")) {
|
|
const newPathParts = newPath.split("?")
|
|
const newSearchParams = new URLSearchParams(newPathParts[1])
|
|
searchParams.forEach((v, k) => {
|
|
if (!newSearchParams.has(k)) {
|
|
newSearchParams.set(k, v)
|
|
}
|
|
})
|
|
return `${newPathParts[0]}?${newSearchParams}`
|
|
}
|
|
return `${newPath}?${searchParams}`
|
|
}
|
|
|
|
return newPath
|
|
}, [
|
|
href,
|
|
searchParams,
|
|
keepSearchParams,
|
|
appendToCurrentPath,
|
|
currentPageSlug,
|
|
])
|
|
|
|
// TODO: Remove this check (and hook) and only return <Link /> when current web is deleted
|
|
const isExternal = useCheckIfExternalLink(href)
|
|
|
|
const trackClickById = useCallback(() => {
|
|
if (trackingId) {
|
|
trackClick(trackingId, trackingParams)
|
|
}
|
|
}, [trackingId, trackingParams])
|
|
|
|
const linkProps = {
|
|
href: fullUrl,
|
|
className: classNames,
|
|
}
|
|
|
|
return isExternal ? (
|
|
<a
|
|
{...linkProps}
|
|
{...props}
|
|
onClick={(e) => {
|
|
if (onClick) {
|
|
onClick(e)
|
|
}
|
|
}}
|
|
/>
|
|
) : (
|
|
<NextLink
|
|
scroll={scroll}
|
|
prefetch={prefetch}
|
|
onClick={(e) => {
|
|
if (onClick) {
|
|
onClick(e)
|
|
}
|
|
if (trackingId) {
|
|
trackClickById()
|
|
}
|
|
}}
|
|
id={trackingId}
|
|
{...props}
|
|
{...linkProps}
|
|
/>
|
|
)
|
|
}
|