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',