's'
This commit is contained in:
@@ -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() {
|
||||
|
||||
69
app/lib/navigation-loading.ts
Normal file
69
app/lib/navigation-loading.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user