yuken 3 лет назад
Родитель
Сommit
62783b0429

+ 1 - 1
packages/app/src/components/Page/DisplaySwitcher.tsx

@@ -104,7 +104,7 @@ const DisplaySwitcher = (): JSX.Element => {
 
                   <div className="d-none d-lg-block">
                     <div id="revision-toc" className="revision-toc">
-                      {/* <TableOfContents /> */}
+                      <TableOfContents />
                     </div>
                     <ContentLinkButtons />
                   </div>

+ 19 - 8
packages/app/src/components/TableOfContents.tsx

@@ -1,8 +1,11 @@
 import React, { useCallback, useEffect, useState } from 'react';
 
+import ReactMarkdown from 'react-markdown';
+
 import { blinkElem } from '~/client/util/blink-section-header';
 import { addSmoothScrollEvent } from '~/client/util/smooth-scroll';
 import { useIsUserPage } from '~/stores/context';
+import { useTocOptions } from '~/stores/renderer';
 import loggerFactory from '~/utils/logger';
 
 
@@ -17,6 +20,8 @@ const TableOfContents = (): JSX.Element => {
 
   const [tocHtml, setTocHtml] = useState('');
 
+  const { data: rendererOptions } = useTocOptions();
+
   const calcViewHeight = useCallback(() => {
     // calculate absolute top of '#revision-toc' element
     const parentElem = document.querySelector('.grw-side-contents-container');
@@ -64,14 +69,20 @@ const TableOfContents = (): JSX.Element => {
       stickyElemSelector=".grw-side-contents-sticky-container"
       calcViewHeight={calcViewHeight}
     >
-      { tocHtml !== ''
+      <ReactMarkdown {...rendererOptions}>
+        {''}
+      </ReactMarkdown>
+      {/* { tocHtml !== ''
         ? (
-          <div
-            id="revision-toc-content"
-            className="revision-toc-content mb-3"
-            // eslint-disable-next-line react/no-danger
-            dangerouslySetInnerHTML={{ __html: tocHtml }}
-          />
+          // <div
+          //   id="revision-toc-content"
+          //   className="revision-toc-content mb-3"
+          //   // eslint-disable-next-line react/no-danger
+          //   dangerouslySetInnerHTML={{ __html: tocHtml }}
+          // />
+          <ReactMarkdown {...rendererOptions}>
+            {''}
+          </ReactMarkdown>
         )
         : (
           <div
@@ -79,7 +90,7 @@ const TableOfContents = (): JSX.Element => {
             className="revision-toc-content mb-2"
           >
           </div>
-        ) }
+        ) } */}
 
     </StickyStretchableScroller>
   );

+ 2 - 0
packages/app/src/interfaces/services/renderer.ts

@@ -1,3 +1,5 @@
+import { HtmlElementNode } from 'rehype-toc';
+
 import { XssOptionConfig } from '~/services/xss/xssOption';
 
 // export type GrowiHydratedEnv = {

+ 0 - 1
packages/app/src/pages/[[...path]].page.tsx

@@ -186,7 +186,6 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   // useNoCdn(props.noCdn);
   // useIndentSize(props.adminPreferredIndentSize);
   useDisableLinkSharing(props.disableLinkSharing);
-
   useRendererConfig(props.rendererConfig);
   // useRendererSettings(props.rendererSettingsStr != null ? JSON.parse(props.rendererSettingsStr) : undefined);
   // useGrowiRendererConfig(props.growiRendererConfigStr != null ? JSON.parse(props.growiRendererConfigStr) : undefined);

+ 22 - 14
packages/app/src/services/renderer/renderer.tsx

@@ -1,6 +1,6 @@
 import { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
 import slug from 'rehype-slug';
-import toc, { HtmlElementNode } from 'rehype-toc';
+import toc from 'rehype-toc';
 import breaks from 'remark-breaks';
 import emoji from 'remark-emoji';
 import footnotes from 'remark-footnotes';
@@ -207,8 +207,12 @@ const logger = loggerFactory('growi:util:GrowiRenderer');
 
 export type RendererOptions = Partial<ReactMarkdownOptions>;
 
+export interface RendererOptionsCustomizer {
+  (options: RendererOptions): void
+}
+
 export interface ReactMarkdownOptionsGenerator {
-  (config: RendererConfig): RendererOptions
+  (config: RendererConfig, customizer?: (options: RendererOptions) => void): RendererOptions
 }
 
 const generateCommonOptions: ReactMarkdownOptionsGenerator = (config: RendererConfig): RendererOptions => {
@@ -221,11 +225,11 @@ const generateCommonOptions: ReactMarkdownOptionsGenerator = (config: RendererCo
   };
 };
 
-export const generateViewOptions: ReactMarkdownOptionsGenerator = (config: RendererConfig): RendererOptions => {
+export const generateViewOptions: ReactMarkdownOptionsGenerator = (config: RendererConfig, customizer?: RendererOptionsCustomizer): RendererOptions => {
 
   const options = generateCommonOptions(config);
 
-  const { remarkPlugins, rehypePlugins, components } = options;
+  const { remarkPlugins, components } = options;
 
   // add remark plugins
   if (remarkPlugins != null) {
@@ -236,7 +240,16 @@ export const generateViewOptions: ReactMarkdownOptionsGenerator = (config: Rende
     }
   }
 
-  // add rehypePlugins
+  if (customizer != null) {
+    // use rehype-toc and get toc node
+    customizer(options);
+  }
+  // if (rehypePlugins != null) {
+  //   rehypePlugins.push([toc, {
+  //     headings: ['h1', 'h2', 'h3'],
+  //     customizeTOC: config.storeTocNode,
+  //   }]);
+  // }
   // rehypePlugins.push([toc, {
   //   headings: ['h1', 'h2', 'h3'],
   //   customizeTOC: storeTocNode,
@@ -267,7 +280,7 @@ export const generateViewOptions: ReactMarkdownOptionsGenerator = (config: Rende
   return options;
 };
 
-export const generateTocOptions: ReactMarkdownOptionsGenerator = (config: RendererConfig): RendererOptions => {
+export const generateTocOptions: ReactMarkdownOptionsGenerator = (config: RendererConfig, customizer?: RendererOptionsCustomizer): RendererOptions => {
 
   const options = generateCommonOptions(config);
 
@@ -278,14 +291,9 @@ export const generateTocOptions: ReactMarkdownOptionsGenerator = (config: Render
     remarkPlugins.push(emoji);
   }
 
-  // add rehypePlugins
-  if (rehypePlugins != null) {
-    rehypePlugins.push([toc, {
-      headings: ['h1', 'h2', 'h3'],
-      customizeTOC: () => {
-        return;
-      },
-    }]);
+  if (customizer != null) {
+    // use rehype-toc and set toc node
+    customizer(options);
   }
   // renderer.rehypePlugins.push([autoLinkHeadings, {
   //   behavior: 'append',

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

@@ -1,5 +1,6 @@
 import EventEmitter from 'events';
 
+import { HtmlElementNode } from 'rehype-toc';
 import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
@@ -218,6 +219,10 @@ export const useRendererConfig = (initialData?: RendererConfig): SWRResponse<Ren
   return useStaticSWR('growiRendererConfig', initialData);
 };
 
+export const useCurrentPageTocNode = (): SWRResponse<HtmlElementNode, any> => {
+  return useStaticSWR('currentPageTocNode');
+};
+
 export const useIsBlinkedHeaderAtBoot = (initialData?: boolean): SWRResponse<boolean, Error> => {
   return useStaticSWR('isBlinkedAtBoot', initialData);
 };

+ 35 - 5
packages/app/src/stores/renderer.tsx

@@ -1,15 +1,19 @@
+import toc, { HtmlElementNode } from 'rehype-toc';
 import { Key, SWRResponse } from 'swr';
 import useSWRImmutable from 'swr/immutable';
 
+
 import {
   ReactMarkdownOptionsGenerator, RendererOptions,
   generateViewOptions, generatePreviewOptions, generateCommentPreviewOptions, generateOthersOptions, generateTocOptions,
 } from '~/services/renderer/renderer';
 
-import { useRendererConfig } from './context';
+import { useCurrentPageTocNode, useRendererConfig } from './context';
 
 // The base hook with common processes
-const _useOptionsBase = (rendererId: string, generator: ReactMarkdownOptionsGenerator): SWRResponse<RendererOptions, Error> => {
+const _useOptionsBase = (
+    rendererId: string, generator: ReactMarkdownOptionsGenerator, customizer?: (options: RendererOptions) => void,
+): SWRResponse<RendererOptions, Error> => {
   const { data: rendererConfig } = useRendererConfig();
 
   const isAllDataValid = rendererConfig != null;
@@ -21,7 +25,7 @@ const _useOptionsBase = (rendererId: string, generator: ReactMarkdownOptionsGene
   const swrResult = useSWRImmutable<RendererOptions, Error>(key);
 
   if (isAllDataValid && swrResult.data == null) {
-    swrResult.mutate(generator(rendererConfig));
+    swrResult.mutate(generator(rendererConfig, customizer));
   }
 
   // call useSWRImmutable again to foce to update cache
@@ -31,13 +35,39 @@ const _useOptionsBase = (rendererId: string, generator: ReactMarkdownOptionsGene
 export const useViewOptions = (): SWRResponse<RendererOptions, Error> => {
   const key = 'viewOptions';
 
-  return _useOptionsBase(key, generateViewOptions);
+  const { mutate: storeTocNode } = useCurrentPageTocNode();
+
+  const customizer = (options: RendererOptions) => {
+    const { rehypePlugins } = options;
+    // store toc node in global state
+    if (rehypePlugins != null) {
+      rehypePlugins.push([toc, {
+        headings: ['h1', 'h2', 'h3'],
+        customizeTOC: storeTocNode,
+      }]);
+    }
+  };
+
+  return _useOptionsBase(key, generateViewOptions, customizer);
 };
 
 export const useTocOptions = (): SWRResponse<RendererOptions, Error> => {
   const key = 'tocOptions';
 
-  return _useOptionsBase(key, generateTocOptions);
+  const { data: tocNode } = useCurrentPageTocNode();
+
+  const customizer = (options: RendererOptions) => {
+    const { rehypePlugins } = options;
+    // set toc node
+    if (rehypePlugins != null) {
+      rehypePlugins.push([toc, {
+        headings: ['h1', 'h2', 'h3'],
+        customizeTOC: () => tocNode,
+      }]);
+    }
+  };
+
+  return _useOptionsBase(key, generateTocOptions, customizer);
 };
 
 export const usePreviewOptions = (): SWRResponse<RendererOptions, Error> => {