NextLink.tsx 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  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. id?: string,
  22. href?: string,
  23. className?: string,
  24. };
  25. export const NextLink = (props: Props): JSX.Element => {
  26. const {
  27. id, href, children, className, ...rest
  28. } = props;
  29. const { data: siteUrl } = useSiteUrl();
  30. if (href == null) {
  31. return <a className={className}>{children}</a>;
  32. }
  33. // extract 'data-*' props
  34. const dataAttributes = Object.fromEntries(
  35. Object.entries(rest).filter(([key]) => key.startsWith('data-')),
  36. );
  37. // when href is an anchor link
  38. if (isAnchorLink(href)) {
  39. return (
  40. <a id={id} href={href} className={className} {...dataAttributes}>{children}</a>
  41. );
  42. }
  43. if (isExternalLink(href, siteUrl)) {
  44. return (
  45. <a id={id} href={href} className={className} target="_blank" rel="noopener noreferrer" {...dataAttributes}>
  46. {children}&nbsp;<i className='icon-share-alt small'></i>
  47. </a>
  48. );
  49. }
  50. return (
  51. <Link {...rest} href={href} prefetch={false} legacyBehavior>
  52. <a href={href} className={className} {...dataAttributes}>{children}</a>
  53. </Link>
  54. );
  55. };