Files
lcbp3.np-dms.work/frontend/node_modules/next/dist/esm/client/link.js
2025-09-21 20:29:15 +07:00

418 lines
18 KiB
JavaScript
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { jsx as _jsx } from "react/jsx-runtime";
import React, { createContext, useContext } from 'react';
import { resolveHref } from './resolve-href';
import { isLocalURL } from '../shared/lib/router/utils/is-local-url';
import { formatUrl } from '../shared/lib/router/utils/format-url';
import { isAbsoluteUrl } from '../shared/lib/utils';
import { addLocale } from './add-locale';
import { RouterContext } from '../shared/lib/router-context.shared-runtime';
import { useIntersection } from './use-intersection';
import { getDomainLocale } from './get-domain-locale';
import { addBasePath } from './add-base-path';
import { useMergedRef } from './use-merged-ref';
import { errorOnce } from '../shared/lib/utils/error-once';
const prefetched = new Set();
function prefetch(router, href, as, options) {
if (typeof window === 'undefined') {
return;
}
if (!isLocalURL(href)) {
return;
}
// We should only dedupe requests when experimental.optimisticClientCache is
// disabled.
if (!options.bypassPrefetchedCheck) {
const locale = // Let the link's locale prop override the default router locale.
typeof options.locale !== 'undefined' ? options.locale : 'locale' in router ? router.locale : undefined;
const prefetchedKey = href + '%' + as + '%' + locale;
// If we've already fetched the key, then don't prefetch it again!
if (prefetched.has(prefetchedKey)) {
return;
}
// Mark this URL as prefetched.
prefetched.add(prefetchedKey);
}
// Prefetch the JSON page if asked (only in the client)
// We need to handle a prefetch error here since we may be
// loading with priority which can reject but we don't
// want to force navigation since this is only a prefetch
router.prefetch(href, as, options).catch((err)=>{
if (process.env.NODE_ENV !== 'production') {
// rethrow to show invalid URL errors
throw err;
}
});
}
function isModifiedEvent(event) {
const eventTarget = event.currentTarget;
const target = eventTarget.getAttribute('target');
return target && target !== '_self' || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || // triggers resource download
event.nativeEvent && event.nativeEvent.which === 2;
}
function linkClicked(e, router, href, as, replace, shallow, scroll, locale, onNavigate) {
const { nodeName } = e.currentTarget;
// anchors inside an svg have a lowercase nodeName
const isAnchorNodeName = nodeName.toUpperCase() === 'A';
if (isAnchorNodeName && isModifiedEvent(e) || e.currentTarget.hasAttribute('download')) {
// ignore click for browsers default behavior
return;
}
if (!isLocalURL(href)) {
if (replace) {
// browser default behavior does not replace the history state
// so we need to do it manually
e.preventDefault();
location.replace(href);
}
// ignore click for browsers default behavior
return;
}
e.preventDefault();
const navigate = ()=>{
if (onNavigate) {
let isDefaultPrevented = false;
onNavigate({
preventDefault: ()=>{
isDefaultPrevented = true;
}
});
if (isDefaultPrevented) {
return;
}
}
// If the router is an NextRouter instance it will have `beforePopState`
const routerScroll = scroll != null ? scroll : true;
if ('beforePopState' in router) {
router[replace ? 'replace' : 'push'](href, as, {
shallow,
locale,
scroll: routerScroll
});
} else {
router[replace ? 'replace' : 'push'](as || href, {
scroll: routerScroll
});
}
};
navigate();
}
function formatStringOrUrl(urlObjOrString) {
if (typeof urlObjOrString === 'string') {
return urlObjOrString;
}
return formatUrl(urlObjOrString);
}
/**
* A React component that extends the HTML `<a>` element to provide [prefetching](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching)
* and client-side navigation between routes.
*
* It is the primary way to navigate between routes in Next.js.
*
* Read more: [Next.js docs: `<Link>`](https://nextjs.org/docs/app/api-reference/components/link)
*/ const Link = /*#__PURE__*/ React.forwardRef(function LinkComponent(props, forwardedRef) {
let children;
const { href: hrefProp, as: asProp, children: childrenProp, prefetch: prefetchProp = null, passHref, replace, shallow, scroll, locale, onClick, onNavigate, onMouseEnter: onMouseEnterProp, onTouchStart: onTouchStartProp, legacyBehavior = false, ...restProps } = props;
children = childrenProp;
if (legacyBehavior && (typeof children === 'string' || typeof children === 'number')) {
children = /*#__PURE__*/ _jsx("a", {
children: children
});
}
const router = React.useContext(RouterContext);
const prefetchEnabled = prefetchProp !== false;
if (process.env.NODE_ENV !== 'production') {
function createPropError(args) {
return Object.defineProperty(new Error("Failed prop type: The prop `" + args.key + "` expects a " + args.expected + " in `<Link>`, but got `" + args.actual + "` instead." + (typeof window !== 'undefined' ? "\nOpen your browser's console to view the Component stack trace." : '')), "__NEXT_ERROR_CODE", {
value: "E319",
enumerable: false,
configurable: true
});
}
// TypeScript trick for type-guarding:
const requiredPropsGuard = {
href: true
};
const requiredProps = Object.keys(requiredPropsGuard);
requiredProps.forEach((key)=>{
if (key === 'href') {
if (props[key] == null || typeof props[key] !== 'string' && typeof props[key] !== 'object') {
throw createPropError({
key,
expected: '`string` or `object`',
actual: props[key] === null ? 'null' : typeof props[key]
});
}
} else {
// TypeScript trick for type-guarding:
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _ = key;
}
});
// TypeScript trick for type-guarding:
const optionalPropsGuard = {
as: true,
replace: true,
scroll: true,
shallow: true,
passHref: true,
prefetch: true,
locale: true,
onClick: true,
onMouseEnter: true,
onTouchStart: true,
legacyBehavior: true,
onNavigate: true
};
const optionalProps = Object.keys(optionalPropsGuard);
optionalProps.forEach((key)=>{
const valType = typeof props[key];
if (key === 'as') {
if (props[key] && valType !== 'string' && valType !== 'object') {
throw createPropError({
key,
expected: '`string` or `object`',
actual: valType
});
}
} else if (key === 'locale') {
if (props[key] && valType !== 'string') {
throw createPropError({
key,
expected: '`string`',
actual: valType
});
}
} else if (key === 'onClick' || key === 'onMouseEnter' || key === 'onTouchStart' || key === 'onNavigate') {
if (props[key] && valType !== 'function') {
throw createPropError({
key,
expected: '`function`',
actual: valType
});
}
} else if (key === 'replace' || key === 'scroll' || key === 'shallow' || key === 'passHref' || key === 'legacyBehavior') {
if (props[key] != null && valType !== 'boolean') {
throw createPropError({
key,
expected: '`boolean`',
actual: valType
});
}
} else if (key === 'prefetch') {
if (props[key] != null && valType !== 'boolean' && props[key] !== 'auto') {
throw createPropError({
key,
expected: '`boolean | "auto"`',
actual: valType
});
}
} else {
// TypeScript trick for type-guarding:
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _ = key;
}
});
}
const { href, as } = React.useMemo(()=>{
if (!router) {
const resolvedHref = formatStringOrUrl(hrefProp);
return {
href: resolvedHref,
as: asProp ? formatStringOrUrl(asProp) : resolvedHref
};
}
const [resolvedHref, resolvedAs] = resolveHref(router, hrefProp, true);
return {
href: resolvedHref,
as: asProp ? resolveHref(router, asProp) : resolvedAs || resolvedHref
};
}, [
router,
hrefProp,
asProp
]);
const previousHref = React.useRef(href);
const previousAs = React.useRef(as);
// This will return the first child, if multiple are provided it will throw an error
let child;
if (legacyBehavior) {
if (process.env.NODE_ENV === 'development') {
if (onClick) {
console.warn('"onClick" was passed to <Link> with `href` of `' + hrefProp + '` but "legacyBehavior" was set. The legacy behavior requires onClick be set on the child of next/link');
}
if (onMouseEnterProp) {
console.warn('"onMouseEnter" was passed to <Link> with `href` of `' + hrefProp + '` but "legacyBehavior" was set. The legacy behavior requires onMouseEnter be set on the child of next/link');
}
try {
child = React.Children.only(children);
} catch (err) {
if (!children) {
throw Object.defineProperty(new Error("No children were passed to <Link> with `href` of `" + hrefProp + "` but one child is required https://nextjs.org/docs/messages/link-no-children"), "__NEXT_ERROR_CODE", {
value: "E320",
enumerable: false,
configurable: true
});
}
throw Object.defineProperty(new Error("Multiple children were passed to <Link> with `href` of `" + hrefProp + "` but only one child is supported https://nextjs.org/docs/messages/link-multiple-children" + (typeof window !== 'undefined' ? " \nOpen your browser's console to view the Component stack trace." : '')), "__NEXT_ERROR_CODE", {
value: "E266",
enumerable: false,
configurable: true
});
}
} else {
child = React.Children.only(children);
}
} else {
if (process.env.NODE_ENV === 'development') {
if ((children == null ? void 0 : children.type) === 'a') {
throw Object.defineProperty(new Error('Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.\nLearn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor'), "__NEXT_ERROR_CODE", {
value: "E209",
enumerable: false,
configurable: true
});
}
}
}
const childRef = legacyBehavior ? child && typeof child === 'object' && child.ref : forwardedRef;
const [setIntersectionRef, isVisible, resetVisible] = useIntersection({
rootMargin: '200px'
});
const setIntersectionWithResetRef = React.useCallback((el)=>{
// Before the link getting observed, check if visible state need to be reset
if (previousAs.current !== as || previousHref.current !== href) {
resetVisible();
previousAs.current = as;
previousHref.current = href;
}
setIntersectionRef(el);
}, [
as,
href,
resetVisible,
setIntersectionRef
]);
const setRef = useMergedRef(setIntersectionWithResetRef, childRef);
// Prefetch the URL if we haven't already and it's visible.
React.useEffect(()=>{
// in dev, we only prefetch on hover to avoid wasting resources as the prefetch will trigger compiling the page.
if (process.env.NODE_ENV !== 'production') {
return;
}
if (!router) {
return;
}
// If we don't need to prefetch the URL, don't do prefetch.
if (!isVisible || !prefetchEnabled) {
return;
}
// Prefetch the URL.
prefetch(router, href, as, {
locale
});
}, [
as,
href,
isVisible,
locale,
prefetchEnabled,
router == null ? void 0 : router.locale,
router
]);
const childProps = {
ref: setRef,
onClick (e) {
if (process.env.NODE_ENV !== 'production') {
if (!e) {
throw Object.defineProperty(new Error('Component rendered inside next/link has to pass click event to "onClick" prop.'), "__NEXT_ERROR_CODE", {
value: "E312",
enumerable: false,
configurable: true
});
}
}
if (!legacyBehavior && typeof onClick === 'function') {
onClick(e);
}
if (legacyBehavior && child.props && typeof child.props.onClick === 'function') {
child.props.onClick(e);
}
if (!router) {
return;
}
if (e.defaultPrevented) {
return;
}
linkClicked(e, router, href, as, replace, shallow, scroll, locale, onNavigate);
},
onMouseEnter (e) {
if (!legacyBehavior && typeof onMouseEnterProp === 'function') {
onMouseEnterProp(e);
}
if (legacyBehavior && child.props && typeof child.props.onMouseEnter === 'function') {
child.props.onMouseEnter(e);
}
if (!router) {
return;
}
prefetch(router, href, as, {
locale,
priority: true,
// @see {https://github.com/vercel/next.js/discussions/40268?sort=top#discussioncomment-3572642}
bypassPrefetchedCheck: true
});
},
onTouchStart: process.env.__NEXT_LINK_NO_TOUCH_START ? undefined : function onTouchStart(e) {
if (!legacyBehavior && typeof onTouchStartProp === 'function') {
onTouchStartProp(e);
}
if (legacyBehavior && child.props && typeof child.props.onTouchStart === 'function') {
child.props.onTouchStart(e);
}
if (!router) {
return;
}
prefetch(router, href, as, {
locale,
priority: true,
// @see {https://github.com/vercel/next.js/discussions/40268?sort=top#discussioncomment-3572642}
bypassPrefetchedCheck: true
});
}
};
// If child is an <a> tag and doesn't have a href attribute, or if the 'passHref' property is
// defined, we specify the current 'href', so that repetition is not needed by the user.
// If the url is absolute, we can bypass the logic to prepend the domain and locale.
if (isAbsoluteUrl(as)) {
childProps.href = as;
} else if (!legacyBehavior || passHref || child.type === 'a' && !('href' in child.props)) {
const curLocale = typeof locale !== 'undefined' ? locale : router == null ? void 0 : router.locale;
// we only render domain locales if we are currently on a domain locale
// so that locale links are still visitable in development/preview envs
const localeDomain = (router == null ? void 0 : router.isLocaleDomain) && getDomainLocale(as, curLocale, router == null ? void 0 : router.locales, router == null ? void 0 : router.domainLocales);
childProps.href = localeDomain || addBasePath(addLocale(as, curLocale, router == null ? void 0 : router.defaultLocale));
}
if (legacyBehavior) {
if (process.env.NODE_ENV === 'development') {
errorOnce('`legacyBehavior` is deprecated and will be removed in a future ' + 'release. A codemod is available to upgrade your components:\n\n' + 'npx @next/codemod@latest new-link .\n\n' + 'Learn more: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#remove-a-tags-from-link-components');
}
return /*#__PURE__*/ React.cloneElement(child, childProps);
}
return /*#__PURE__*/ _jsx("a", {
...restProps,
...childProps,
children: children
});
});
const LinkStatusContext = /*#__PURE__*/ createContext({
// We do not support link status in the Pages Router, so we always return false
pending: false
});
export const useLinkStatus = ()=>{
// This behaviour is like React's useFormStatus. When the component is not under
// a <form> tag, it will get the default value, instead of throwing an error.
return useContext(LinkStatusContext);
};
export default Link;
//# sourceMappingURL=link.js.map