From 200823c437e01d7c527c9224dc74b5c317291372 Mon Sep 17 00:00:00 2001
From: zheng <zhouzheng0646@qq.com>
Date: Sat, 10 Jun 2023 18:07:49 +0800
Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=96=87?=
 =?UTF-8?q?=E6=A1=A3=E9=94=9A=E7=82=B9=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/Layouts/Anchor/hooks.js    | 19 ++++
 .../src/components/Layouts/Anchor/index.jsx   | 92 +++++++++++++++++++
 .../src/components/Layouts/index/index.tsx    |  9 +-
 examples/website/src/menus.ts                 | 10 +-
 examples/website/src/pages/update/README.md   |  6 +-
 5 files changed, 126 insertions(+), 10 deletions(-)
 create mode 100644 examples/website/src/components/Layouts/Anchor/hooks.js
 create mode 100644 examples/website/src/components/Layouts/Anchor/index.jsx

diff --git a/examples/website/src/components/Layouts/Anchor/hooks.js b/examples/website/src/components/Layouts/Anchor/hooks.js
new file mode 100644
index 000000000..75da23188
--- /dev/null
+++ b/examples/website/src/components/Layouts/Anchor/hooks.js
@@ -0,0 +1,19 @@
+import { useLocation } from '@kkt/pro';
+import { useMemo } from 'react';
+
+export function useComponentMarkdown() {
+  const { pathname } = useLocation();
+  const mdText = useMemo(() => {
+    const componentName = pathname?.split('/').at(-1);
+    if (componentName) {
+      try {
+        const mdText = require(`../../../pages/${componentName}/README.md`)
+          ?.default?.source;
+        return mdText;
+      } catch {}
+    }
+    return '';
+  }, [pathname]);
+
+  return mdText;
+}
diff --git a/examples/website/src/components/Layouts/Anchor/index.jsx b/examples/website/src/components/Layouts/Anchor/index.jsx
new file mode 100644
index 000000000..682f0c678
--- /dev/null
+++ b/examples/website/src/components/Layouts/Anchor/index.jsx
@@ -0,0 +1,92 @@
+import { Anchor } from 'antd';
+import { useEffect, useState } from 'react';
+import remark from 'remark';
+import { useComponentMarkdown } from './hooks';
+
+export default function Anchors() {
+  const markdownText = useComponentMarkdown();
+  const [headers, setHeaders] = useState([]);
+
+  function traverse(node, headers = []) {
+    let currentHeader = null;
+
+    if (node.type === 'heading') {
+      // 英文转小写,去首尾空白和标点符号,中间空格替换成-
+      const text = node.children[0].value;
+      const href = text
+        ?.toLowerCase()
+        .replace(/^[^\w\u4e00-\u9fa5]+|[^\w\u4e00-\u9fa5]+$/g, '')
+        .replace(/\s+/g, '-')
+        .replace(/\./g, '');
+
+      const level = node.depth;
+      currentHeader = { text, href, level, children: [] };
+
+      if (level === 1) {
+        headers.push(currentHeader);
+      } else {
+        const parentHeader = headers[headers.length - 1];
+        if (parentHeader) {
+          parentHeader.children.push(currentHeader);
+        }
+      }
+    }
+
+    if (node.children) {
+      for (let i = 0; i < node.children.length; i++) {
+        traverse(
+          node.children[i],
+          currentHeader ? currentHeader.children : headers,
+        );
+      }
+    }
+
+    return headers;
+  }
+
+  useEffect(() => {
+    const processor = remark();
+    const ast = processor.parse(markdownText);
+    const headers = traverse(ast);
+    setHeaders(headers);
+  }, [markdownText]);
+
+  function createItems(headers) {
+    return headers.map((header) => {
+      const item = {
+        key: `${header.text}`,
+        href: `${header.href}`,
+        title: header.text,
+      };
+
+      if (header.children.length > 0) {
+        item.children = createItems(header.children);
+      }
+
+      return item;
+    });
+  }
+
+  const items = createItems(headers);
+
+  // ... 使用 items 渲染你的锚点 ...
+
+  return (
+    <Anchor
+      items={items}
+      onClick={(e, link) => {
+        e.preventDefault();
+        const element = document.getElementById(link.href);
+        element?.scrollIntoView({ behavior: 'instant', block: 'start' });
+        // console.log('element.offsetTop: ', window.scrollTo, element.offsetTop);
+        // if (element) {
+        //   window.scrollTo({
+        //     top: element.offsetTop - 58,
+        //     left: 0,
+        //     behavior: 'smooth'
+        //   });
+        // }
+      }}
+    />
+  );
+}
diff --git a/examples/website/src/components/Layouts/index/index.tsx b/examples/website/src/components/Layouts/index/index.tsx
index e60c61d21..a3e1917dd 100644
--- a/examples/website/src/components/Layouts/index/index.tsx
+++ b/examples/website/src/components/Layouts/index/index.tsx
@@ -6,15 +6,18 @@ import React, { useState, useEffect } from 'react'
 import { ConfigProvider, theme } from 'antd'
 import '@wcj/dark-mode';
 import HomeFooter from './HomeFooter';
+import AnchorRight from '../Anchor';
 
 export default function Layout(props: KktproPageProps) {
   const { pathname } = useLocation();
   const [data_theme, setDataTheme] = useState('light')
+
   useEffect(() => {
     document.addEventListener('colorschemechange', (e) => {
       setDataTheme(e.detail.colorScheme)
     });
   }, []);
+
   return (
     <Wrapper className="wmde-markdown-var">
       <Navbar />
@@ -32,9 +35,11 @@ export default function Layout(props: KktproPageProps) {
             ) : (
               <React.Fragment>
                 <Menu />
-
                 <div style={{ paddingLeft: 240, paddingTop: 58, height: '100%' }}>
                   <Outlet />
+                  <div style={{ position: 'fixed', marginLeft: 1150, top: 100 }}>
+                    <AnchorRight />
+                  </div>
                 </div>
               </React.Fragment>
             )}
@@ -42,6 +47,6 @@ export default function Layout(props: KktproPageProps) {
           {pathname === '/home' && <HomeFooter />}
         </Main>
       </ConfigProvider>
-    </Wrapper>
+    </Wrapper >
   );
 }
diff --git a/examples/website/src/menus.ts b/examples/website/src/menus.ts
index f6dbcb692..daf6dc4a1 100644
--- a/examples/website/src/menus.ts
+++ b/examples/website/src/menus.ts
@@ -16,11 +16,11 @@ export const menusDocsConfig: MenusConfigObject[] = [
   },
   {
     title: '更新日志',
-    path:'/docs/change-log'
+    path: '/docs/change-log'
   },
   {
     title: '从v1到v2',
-    path:'/docs/update'
+    path: '/docs/update'
   },
   {
     divider: true,
@@ -144,15 +144,15 @@ export const menusComponentsConfig: MenusConfigObject[] = [
     path: '/components/formdetail',
   },
   {
-    title:'UploadGrid - 拖拽上传',
+    title: 'UploadGrid - 拖拽上传',
     path: '/components/uploadgrid',
   },
   {
-    title:'Edit-Table - 编辑表格',
+    title: 'Edit-Table - 编辑表格',
     path: '/components/edit-table',
   },
   {
-    title:'Fuzzy-Query - 模糊查询',
+    title: 'Fuzzy-Query - 模糊查询',
     path: '/components/fuzzy-query',
   },
 ];
diff --git a/examples/website/src/pages/update/README.md b/examples/website/src/pages/update/README.md
index 864d403cb..c29ada62f 100644
--- a/examples/website/src/pages/update/README.md
+++ b/examples/website/src/pages/update/README.md
@@ -120,7 +120,7 @@ export default function Page(props) {
     }}>
     </div>
   );
-} 
+}
 ```
 #### location
 ```diff
@@ -159,7 +159,7 @@ class Page extends Component {
     return (
       <div>
 -        {this.props.match.type}
-+        {this.match.type} 
++        {this.match.type}
       </div>
     )
   }
@@ -214,4 +214,4 @@ pattern: {path: 'list/search/:type'}
 - const { query } = history.location;
 + import { parse } from 'query-string';
 + const query = parse(history.location.search);
-```
\ No newline at end of file
+```

From b4c06f060566e232a7ec4e8efd755d3cad4605a1 Mon Sep 17 00:00:00 2001
From: zheng <zhouzheng0646@qq.com>
Date: Sat, 10 Jun 2023 18:27:49 +0800
Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/Layouts/Anchor/index.jsx    | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)

diff --git a/examples/website/src/components/Layouts/Anchor/index.jsx b/examples/website/src/components/Layouts/Anchor/index.jsx
index 682f0c678..bb1f1af38 100644
--- a/examples/website/src/components/Layouts/Anchor/index.jsx
+++ b/examples/website/src/components/Layouts/Anchor/index.jsx
@@ -5,7 +5,7 @@ import { useComponentMarkdown } from './hooks';
 
 export default function Anchors() {
   const markdownText = useComponentMarkdown();
-  const [headers, setHeaders] = useState([]);
+  const [items, setItems] = useState([]);
 
   function traverse(node, headers = []) {
     let currentHeader = null;
@@ -48,7 +48,9 @@ export default function Anchors() {
     const processor = remark();
     const ast = processor.parse(markdownText);
     const headers = traverse(ast);
-    setHeaders(headers);
+
+    const items = createItems(headers);
+    setItems(items);
   }, [markdownText]);
 
   function createItems(headers) {
@@ -67,10 +69,6 @@ export default function Anchors() {
     });
   }
 
-  const items = createItems(headers);
-
-  // ... 使用 items 渲染你的锚点 ...
-
   return (
     <Anchor
       items={items}
@@ -78,14 +76,6 @@ export default function Anchors() {
         e.preventDefault();
         const element = document.getElementById(link.href);
         element?.scrollIntoView({ behavior: 'instant', block: 'start' });
-        // console.log('element.offsetTop: ', window.scrollTo, element.offsetTop);
-        // if (element) {
-        //   window.scrollTo({
-        //     top: element.offsetTop - 58,
-        //     left: 0,
-        //     behavior: 'smooth'
-        //   });
-        // }
       }}
     />
   );

From beba52b35c9b1f98ce0991520ac0f0eaba830809 Mon Sep 17 00:00:00 2001
From: zheng <zhouzheng0646@qq.com>
Date: Mon, 12 Jun 2023 11:01:43 +0800
Subject: [PATCH 3/3] =?UTF-8?q?feat:=20=E6=A0=B9=E6=8D=AEMD=E6=96=87?=
 =?UTF-8?q?=E4=BB=B6=E6=A0=87=E9=A2=98=E7=BA=A7=E5=88=AB=E5=B1=95=E7=A4=BA?=
 =?UTF-8?q?=E5=87=BA=E9=94=9A=E7=82=B9=E5=B1=82=E7=BA=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/components/Layouts/index/index.tsx    |  4 --
 .../{Layouts => Preview}/Anchor/hooks.js      |  4 +-
 .../{Layouts => Preview}/Anchor/index.jsx     | 38 +++++++++----------
 .../website/src/components/Preview/index.tsx  |  6 +++
 .../src/components/Preview/useHyperlink.tsx   |  2 +-
 5 files changed, 28 insertions(+), 26 deletions(-)
 rename examples/website/src/components/{Layouts => Preview}/Anchor/hooks.js (78%)
 rename examples/website/src/components/{Layouts => Preview}/Anchor/index.jsx (68%)

diff --git a/examples/website/src/components/Layouts/index/index.tsx b/examples/website/src/components/Layouts/index/index.tsx
index a3e1917dd..90fb4e52b 100644
--- a/examples/website/src/components/Layouts/index/index.tsx
+++ b/examples/website/src/components/Layouts/index/index.tsx
@@ -6,7 +6,6 @@ import React, { useState, useEffect } from 'react'
 import { ConfigProvider, theme } from 'antd'
 import '@wcj/dark-mode';
 import HomeFooter from './HomeFooter';
-import AnchorRight from '../Anchor';
 
 export default function Layout(props: KktproPageProps) {
   const { pathname } = useLocation();
@@ -37,9 +36,6 @@ export default function Layout(props: KktproPageProps) {
                 <Menu />
                 <div style={{ paddingLeft: 240, paddingTop: 58, height: '100%' }}>
                   <Outlet />
-                  <div style={{ position: 'fixed', marginLeft: 1150, top: 100 }}>
-                    <AnchorRight />
-                  </div>
                 </div>
               </React.Fragment>
             )}
diff --git a/examples/website/src/components/Layouts/Anchor/hooks.js b/examples/website/src/components/Preview/Anchor/hooks.js
similarity index 78%
rename from examples/website/src/components/Layouts/Anchor/hooks.js
rename to examples/website/src/components/Preview/Anchor/hooks.js
index 75da23188..600582340 100644
--- a/examples/website/src/components/Layouts/Anchor/hooks.js
+++ b/examples/website/src/components/Preview/Anchor/hooks.js
@@ -7,8 +7,8 @@ export function useComponentMarkdown() {
     const componentName = pathname?.split('/').at(-1);
     if (componentName) {
       try {
-        const mdText = require(`../../../pages/${componentName}/README.md`)
-          ?.default?.source;
+        const mdText = require(`src/pages/${componentName}/README.md`)?.default
+          ?.source;
         return mdText;
       } catch {}
     }
diff --git a/examples/website/src/components/Layouts/Anchor/index.jsx b/examples/website/src/components/Preview/Anchor/index.jsx
similarity index 68%
rename from examples/website/src/components/Layouts/Anchor/index.jsx
rename to examples/website/src/components/Preview/Anchor/index.jsx
index bb1f1af38..e913abc20 100644
--- a/examples/website/src/components/Layouts/Anchor/index.jsx
+++ b/examples/website/src/components/Preview/Anchor/index.jsx
@@ -1,13 +1,11 @@
 import { Anchor } from 'antd';
 import { useEffect, useState } from 'react';
 import remark from 'remark';
-import { useComponentMarkdown } from './hooks';
 
-export default function Anchors() {
-  const markdownText = useComponentMarkdown();
+export default function Anchors({ markdownText }) {
   const [items, setItems] = useState([]);
 
-  function traverse(node, headers = []) {
+  function traverse(node, stack = [{ children: [] }]) {
     let currentHeader = null;
 
     if (node.type === 'heading') {
@@ -15,40 +13,41 @@ export default function Anchors() {
       const text = node.children[0].value;
       const href = text
         ?.toLowerCase()
-        .replace(/^[^\w\u4e00-\u9fa5]+|[^\w\u4e00-\u9fa5]+$/g, '')
-        .replace(/\s+/g, '-')
-        .replace(/\./g, '');
+        .replace(/^[^\w\u4e00-\u9fa5]+|[^\w\u4e00-\u9fa5]+$|[\n+/\/@\.]/g, '')
+        .replace(/\s+/g, '-');
 
       const level = node.depth;
       currentHeader = { text, href, level, children: [] };
 
-      if (level === 1) {
-        headers.push(currentHeader);
-      } else {
-        const parentHeader = headers[headers.length - 1];
-        if (parentHeader) {
-          parentHeader.children.push(currentHeader);
+      let top = stack[stack.length - 1];
+
+      while (stack.length > level) {
+        stack.pop();
+        top = stack[stack.length - 1];
+      }
+
+      if (level > stack.length) {
+        if (top.children.length > 0) {
+          stack.push(top.children[top.children.length - 1]);
         }
       }
+
+      top.children.push(currentHeader);
     }
 
     if (node.children) {
       for (let i = 0; i < node.children.length; i++) {
-        traverse(
-          node.children[i],
-          currentHeader ? currentHeader.children : headers,
-        );
+        traverse(node.children[i], stack);
       }
     }
 
-    return headers;
+    return stack[0].children;
   }
 
   useEffect(() => {
     const processor = remark();
     const ast = processor.parse(markdownText);
     const headers = traverse(ast);
-
     const items = createItems(headers);
     setItems(items);
   }, [markdownText]);
@@ -73,6 +72,7 @@ export default function Anchors() {
     <Anchor
       items={items}
       onClick={(e, link) => {
+        console.log('link: ', link);
         e.preventDefault();
         const element = document.getElementById(link.href);
         element?.scrollIntoView({ behavior: 'instant', block: 'start' });
diff --git a/examples/website/src/components/Preview/index.tsx b/examples/website/src/components/Preview/index.tsx
index 502976395..189b76030 100644
--- a/examples/website/src/components/Preview/index.tsx
+++ b/examples/website/src/components/Preview/index.tsx
@@ -17,6 +17,8 @@ import Loading from '../Loading';
 import BackToUp from '@uiw/react-back-to-top';
 import Footer from "../Footer"
 import './nodes/toc.less';
+import { useHyperlink } from './useHyperlink';
+import Anchors from './Anchor';
 
 const Preview = CodeLayout.Preview;
 const Code = CodeLayout.Code;
@@ -58,6 +60,7 @@ const Markdown = styled<
 const PreviewDocument = ({ path, editePath }: { path: MdDataHandle, editePath: string; }) => {
   const $dom = useRef<HTMLDivElement>(null);
   const { mdData, loading } = useMdData(path);
+  useHyperlink($dom.current);
   return (
     <Wrapper ref={$dom}>
       <Loading loading={loading}>
@@ -151,6 +154,9 @@ const PreviewDocument = ({ path, editePath }: { path: MdDataHandle, editePath: s
       <BackToUp element={$dom.current} style={{ float: 'right' }}>
         Top
       </BackToUp>
+      <div style={{ position: 'fixed', marginLeft: 1150, top: 100 }}>
+        <Anchors markdownText={mdData.source} />
+      </div>
     </Wrapper>
   );
 };
diff --git a/examples/website/src/components/Preview/useHyperlink.tsx b/examples/website/src/components/Preview/useHyperlink.tsx
index dc67b8b33..f73ecb8fa 100644
--- a/examples/website/src/components/Preview/useHyperlink.tsx
+++ b/examples/website/src/components/Preview/useHyperlink.tsx
@@ -43,7 +43,7 @@ export const useHyperlink = (dom: HTMLDivElement | null) => {
           target.classList.add('active');
         }
       };
-      const scrollFn = () => {};
+      const scrollFn = () => { };
       const scrollEndFn = () => {
         const $main = document.getElementsByTagName(
           'main',