NextLink.tsx 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  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. const isAttached = (href: string): boolean => {
  20. return href.startsWith('/attachment/');
  21. };
  22. type Props = Omit<LinkProps, 'href'> & {
  23. children: React.ReactNode,
  24. href?: string,
  25. className?: string,
  26. };
  27. export const NextLink = (props: Props): JSX.Element => {
  28. const {
  29. href, children, className, ...rest
  30. } = props;
  31. const { data: siteUrl } = useSiteUrl();
  32. if (href == null) {
  33. return <a className={className}>{children}</a>;
  34. }
  35. // extract 'data-*' props
  36. const dataAttributes = Object.fromEntries(
  37. Object.entries(rest).filter(([key]) => key.startsWith('data-')),
  38. );
  39. // when href is an anchor link
  40. if (isAnchorLink(href)) {
  41. return (
  42. <a href={href} className={className} {...dataAttributes}>{children}</a>
  43. );
  44. }
  45. if (isExternalLink(href, siteUrl)) {
  46. return (
  47. <a href={href} className={className} target="_blank" rel="noopener noreferrer" {...dataAttributes}>
  48. {children}&nbsp;<i className='icon-share-alt small'></i>
  49. </a>
  50. );
  51. }
  52. // when href is an attachment file
  53. if (isAttached(href)) {
  54. const dlhref = href.replace('/attachment/', '/download/');
  55. return (
  56. <span>
  57. <a href={href} className={className} target="_blank" rel="noopener noreferrer" {...dataAttributes}>
  58. {children}
  59. </a>&nbsp;
  60. <a href={dlhref} className="attachment-download"><i className='icon-cloud-download'></i></a>
  61. </span>
  62. );
  63. }
  64. return (
  65. <Link {...rest} href={href} prefetch={false} legacyBehavior>
  66. <a href={href} className={className} {...dataAttributes}>{children}</a>
  67. </Link>
  68. );
  69. };