Просмотр исходного кода

Merge pull request #10566 from growilabs/imprv/next-link-target-attribute

imprv: Support target attribute for anchor links
Yuki Takei 4 месяцев назад
Родитель
Сommit
21ac1635f1

+ 20 - 13
apps/app/src/components/ReactMarkdownComponents/NextLink.tsx

@@ -1,4 +1,4 @@
-import type { JSX } from 'react';
+import type { AnchorHTMLAttributes, JSX } from 'react';
 import type { LinkProps } from 'next/link';
 import Link from 'next/link';
 import { pagePathUtils } from '@growi/core/dist/utils';
@@ -8,7 +8,7 @@ import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:components:NextLink');
 
-const isAnchorLink = (href: string): boolean => {
+const hasAnchorLink = (href: string): boolean => {
   return href.toString().length > 0 && href[0] === '#';
 };
 
@@ -34,15 +34,16 @@ const isCreatablePage = (href: string) => {
   }
 };
 
-type Props = Omit<LinkProps, 'href'> & {
-  children: React.ReactNode;
-  id?: string;
-  href?: string;
-  className?: string;
-};
+type Props = AnchorHTMLAttributes<HTMLAnchorElement> &
+  Omit<LinkProps, 'href'> & {
+    children: React.ReactNode;
+    id?: string;
+    href?: string;
+    className?: string;
+  };
 
 export const NextLink = (props: Props): JSX.Element => {
-  const { id, href, children, className, onClick, ...rest } = props;
+  const { id, href, children, className, target, onClick, ...rest } = props;
 
   const siteUrl = useSiteUrl();
 
@@ -56,7 +57,7 @@ export const NextLink = (props: Props): JSX.Element => {
     Object.entries(rest).filter(([key]) => key.startsWith('data-')),
   );
 
-  if (isExternalLink(href, siteUrl)) {
+  if (isExternalLink(href, siteUrl) || target === '_blank') {
     return (
       <a
         id={id}
@@ -67,19 +68,25 @@ export const NextLink = (props: Props): JSX.Element => {
         rel="noopener noreferrer"
         {...dataAttributes}
       >
-        {children}&nbsp;
-        <span className="growi-custom-icons">external_link</span>
+        {children}
+        {target === '_blank' && (
+          <span style={{ userSelect: 'none' }}>
+            &nbsp;
+            <span className="growi-custom-icons">external_link</span>
+          </span>
+        )}
       </a>
     );
   }
 
   // when href is an anchor link or not-creatable path
-  if (isAnchorLink(href) || !isCreatablePage(href)) {
+  if (hasAnchorLink(href) || !isCreatablePage(href) || target != null) {
     return (
       <a
         id={id}
         href={href}
         className={className}
+        target={target}
         onClick={onClick}
         {...dataAttributes}
       >

+ 1 - 0
apps/app/src/services/renderer/recommended-whitelist.ts

@@ -59,6 +59,7 @@ export const tagNames: Array<string> = [
 ];
 
 export const attributes: Attributes = deepmerge(relaxedSchemaAttributes, {
+  a: ['target'],
   iframe: ['allow', 'referrerpolicy', 'sandbox', 'src'],
   video: ['controls', 'src', 'muted', 'preload', 'width', 'height', 'autoplay'],
   // The special value 'data*' as a property name can be used to allow all data properties.