This commit is contained in:
eric
2026-06-08 06:49:21 -05:00
parent 06ab949d81
commit 874941575c
9 changed files with 364 additions and 9 deletions

View File

@@ -3,16 +3,32 @@
import NextLink from "next/link";
import { usePathname as useNextPathname } from "next/navigation";
import { useLocale } from "../context/LocaleContext";
import {
isInternalNavigationClick,
startNavigation,
} from "@/app/lib/navigation-loading";
type LocaleLinkProps = React.ComponentProps<typeof NextLink>;
export function LocaleLink({ href, ...props }: LocaleLinkProps) {
export function LocaleLink({ href, onClick, ...props }: LocaleLinkProps) {
const locale = useLocale();
const hrefStr = typeof href === "string" ? href : href.pathname ?? "/";
const localeHref = hrefStr.startsWith("/")
? `/${locale}${hrefStr === "/" ? "" : hrefStr}`
: hrefStr;
return <NextLink href={localeHref} {...props} />;
return (
<NextLink
href={localeHref}
onClick={(event) => {
if (isInternalNavigationClick(event.nativeEvent, event.currentTarget)) {
startNavigation();
}
onClick?.(event);
}}
{...props}
/>
);
}
export function usePathname() {

View File

@@ -0,0 +1,69 @@
type NavigationListener = (loading: boolean) => void;
let listeners = new Set<NavigationListener>();
let loading = false;
function notify() {
listeners.forEach((listener) => listener(loading));
}
export function subscribeNavigationLoading(listener: NavigationListener) {
listeners.add(listener);
listener(loading);
return () => {
listeners.delete(listener);
};
}
export function startNavigation() {
if (loading) return;
loading = true;
notify();
}
export function stopNavigation() {
if (!loading) return;
loading = false;
notify();
}
export function isNavigationLoading() {
return loading;
}
export function isInternalNavigationClick(
event: MouseEvent,
anchor: HTMLAnchorElement
): boolean {
if (event.defaultPrevented) return false;
if (event.button !== 0) return false;
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return false;
if (anchor.target === "_blank") return false;
if (anchor.hasAttribute("download")) return false;
const href = anchor.getAttribute("href");
if (
!href ||
href.startsWith("#") ||
href.startsWith("mailto:") ||
href.startsWith("tel:") ||
href.startsWith("javascript:")
) {
return false;
}
try {
const url = new URL(href, window.location.href);
if (url.origin !== window.location.origin) return false;
if (
url.pathname === window.location.pathname &&
url.search === window.location.search &&
!url.hash
) {
return false;
}
return true;
} catch {
return false;
}
}