فهرست منبع

impl NextLink component

Yuki Takei 3 سال پیش
والد
کامیت
1b0813ad23

+ 39 - 0
packages/app/src/components/ReactMarkdownComponents/NextLink.tsx

@@ -0,0 +1,39 @@
+import Link from 'next/link';
+
+import { useSiteUrl } from '~/stores/context';
+
+type Props = {
+  children: React.ReactNode,
+  href?: string,
+}
+
+const isAnchorLink = (href: string): boolean => {
+  return href.length > 0 && href[0] === '#';
+};
+
+const isExternalLink = (href: string, siteUrl: string | undefined): boolean => {
+  const baseUrl = new URL(siteUrl ?? 'https://example.com');
+  const hrefUrl = new URL(href, baseUrl);
+
+  return baseUrl.host !== hrefUrl.host;
+};
+
+export const NextLink = ({ href, children }: Props): JSX.Element => {
+
+  const { data: siteUrl } = useSiteUrl();
+
+  // when href is an anchor link
+  if (href == null || isAnchorLink(href)) {
+    return <a href={href}>{children}</a>;
+  }
+
+  if (isExternalLink(href, siteUrl)) {
+    return <a href={href} target="_blank" rel="noreferrer">
+      {children}&nbsp;<i className='icon-share-alt small'></i>
+    </a>;
+  }
+
+  return (
+    <Link href={href}><a>{children}</a></Link>
+  );
+};

+ 10 - 11
packages/app/src/components/TableOfContents.jsx

@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
 import PageContainer from '~/client/services/PageContainer';
 import { blinkElem } from '~/client/util/blink-section-header';
 import { addSmoothScrollEvent } from '~/client/util/smooth-scroll';
-import { useGlobalEventEmitter } from '~/stores/context';
 import loggerFactory from '~/utils/logger';
 
 
@@ -26,8 +25,6 @@ const TableOfContents = (props) => {
   const { pageUser } = pageContainer.state;
   const isUserPage = pageUser != null;
 
-  const { data: globalEmitter } = useGlobalEventEmitter();
-
   const [tocHtml, setTocHtml] = useState('');
 
   const calcViewHeight = useCallback(() => {
@@ -56,15 +53,17 @@ const TableOfContents = (props) => {
     addSmoothScrollEvent(anchorsInToc, blinkElem);
   }, [tocHtml]);
 
+  // == TODO: render ToC without globalEmitter -- Yuki Takei
+  //
   // set handler to render ToC
-  useEffect(() => {
-    const handler = html => setTocHtml(html);
-    globalEmitter.on('renderTocHtml', handler);
-
-    return function cleanup() {
-      globalEmitter.removeListener('renderTocHtml', handler);
-    };
-  }, [globalEmitter]);
+  // useEffect(() => {
+  //   const handler = html => setTocHtml(html);
+  //   globalEmitter.on('renderTocHtml', handler);
+
+  //   return function cleanup() {
+  //     globalEmitter.removeListener('renderTocHtml', handler);
+  //   };
+  // }, [globalEmitter]);
 
   return (
     <StickyStretchableScroller

+ 4 - 0
packages/app/src/services/renderer/growi-renderer.ts

@@ -6,6 +6,7 @@ import emoji from 'remark-emoji';
 import footnotes from 'remark-footnotes';
 import gfm from 'remark-gfm';
 
+import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
 import { GrowiRendererConfig, RendererSettings } from '~/interfaces/services/renderer';
 import loggerFactory from '~/utils/logger';
 
@@ -215,6 +216,9 @@ const generateCommonOptions: ReactMarkdownOptionsGenerator = (
   return {
     remarkPlugins: [gfm],
     rehypePlugins: [slug],
+    components: {
+      a: NextLink,
+    },
   };
 };
 

+ 1 - 5
packages/app/src/stores/context.tsx

@@ -5,7 +5,7 @@ import useSWRImmutable from 'swr/immutable';
 
 
 import { SupportedActionType } from '~/interfaces/activity';
-import { CustomWindow } from '~/interfaces/global';
+// import { CustomWindow } from '~/interfaces/global';
 import { GrowiRendererConfig } from '~/interfaces/services/renderer';
 import InterceptorManager from '~/services/interceptor-manager';
 
@@ -18,10 +18,6 @@ import { useStaticSWR } from './use-static-swr';
 type Nullable<T> = T | null;
 
 
-export const useGlobalEventEmitter = (): SWRResponse<EventEmitter, Error> => {
-  return useStaticSWR<EventEmitter, Error>('globalEventEmitter', undefined, { fallbackData: (window as CustomWindow).globalEmitter });
-};
-
 export const useInterceptorManager = (): SWRResponse<InterceptorManager, Error> => {
   return useStaticSWR<InterceptorManager, Error>('interceptorManager', undefined, { fallbackData: new InterceptorManager() });
 };