NextLink.tsx 2.2 KB

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