NextLink.tsx 1.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import Link, { LinkProps } from 'next/link';
  2. import { useSiteUrl } from '~/stores/context';
  3. import loggerFactory from '~/utils/logger';
  4. const logger = loggerFactory('growi:components:NextLink');
  5. const isAnchorLink = (href: string): boolean => {
  6. return href.toString().length > 0 && href[0] === '#';
  7. };
  8. const isExternalLink = (href: string, siteUrl: string | undefined): boolean => {
  9. try {
  10. const baseUrl = new URL(siteUrl ?? 'https://example.com');
  11. const hrefUrl = new URL(href, baseUrl);
  12. return baseUrl.host !== hrefUrl.host;
  13. }
  14. catch (err) {
  15. logger.debug(err);
  16. return false;
  17. }
  18. };
  19. type Props = Omit<LinkProps, 'href'> & {
  20. children: React.ReactNode,
  21. href?: string,
  22. className?: string,
  23. };
  24. export const NextLink = ({
  25. href, children, className, ...props
  26. }: Props): JSX.Element => {
  27. const { data: siteUrl } = useSiteUrl();
  28. if (href == null) {
  29. return <a className={className}>{children}</a>;
  30. }
  31. // when href is an anchor link
  32. if (isAnchorLink(href)) {
  33. return (
  34. <a href={href} className={className}>{children}</a>
  35. );
  36. }
  37. if (isExternalLink(href, siteUrl)) {
  38. return (
  39. <a href={href} className={className} target="_blank" rel="noopener noreferrer">
  40. {children}&nbsp;<i className='icon-share-alt small'></i>
  41. </a>
  42. );
  43. }
  44. return (
  45. <Link {...props} href={href} prefetch={false}>
  46. <a href={href} className={className}>{children}</a>
  47. </Link>
  48. );
  49. };