NextLink.tsx 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. import { pagePathUtils } from '@growi/core/dist/utils';
  2. import type { LinkProps } from 'next/link';
  3. import Link from 'next/link';
  4. import { useSiteUrl } from '~/stores/context';
  5. import loggerFactory from '~/utils/logger';
  6. const logger = loggerFactory('growi:components:NextLink');
  7. const isAnchorLink = (href: string): boolean => {
  8. return href.toString().length > 0 && href[0] === '#';
  9. };
  10. const isExternalLink = (href: string, siteUrl: string | undefined): boolean => {
  11. try {
  12. const baseUrl = new URL(siteUrl ?? 'https://example.com');
  13. const hrefUrl = new URL(href, baseUrl);
  14. return baseUrl.host !== hrefUrl.host;
  15. }
  16. catch (err) {
  17. logger.debug(err);
  18. return false;
  19. }
  20. };
  21. const isCreatablePage = (href: string) => {
  22. try {
  23. const url = new URL(href);
  24. const pathName = url.pathname;
  25. return pagePathUtils.isCreatablePage(pathName);
  26. }
  27. catch (err) {
  28. logger.debug(err);
  29. return false;
  30. }
  31. };
  32. type Props = Omit<LinkProps, 'href'> & {
  33. children: React.ReactNode,
  34. id?: string,
  35. href?: string,
  36. className?: string,
  37. };
  38. export const NextLink = (props: Props): JSX.Element => {
  39. const {
  40. id, href, children, className, ...rest
  41. } = props;
  42. const { data: siteUrl } = useSiteUrl();
  43. if (href == null) {
  44. return <a className={className}>{children}</a>;
  45. }
  46. // extract 'data-*' props
  47. const dataAttributes = Object.fromEntries(
  48. Object.entries(rest).filter(([key]) => key.startsWith('data-')),
  49. );
  50. if (isExternalLink(href, siteUrl)) {
  51. return (
  52. <a id={id} href={href} className={className} target="_blank" rel="noopener noreferrer" {...dataAttributes}>
  53. {children}&nbsp;<span className="growi-custom-icons">external_link</span>
  54. </a>
  55. );
  56. }
  57. // when href is an anchor link or not-creatable path
  58. if (isAnchorLink(href) || !isCreatablePage(href)) {
  59. return (
  60. <a id={id} href={href} className={className} {...dataAttributes}>{children}</a>
  61. );
  62. }
  63. return (
  64. <Link {...rest} href={href} prefetch={false} legacyBehavior>
  65. <a href={href} className={className} {...dataAttributes}>{children}</a>
  66. </Link>
  67. );
  68. };