Skip to content

Commit

Permalink
feat: 支持搜索
Browse files Browse the repository at this point in the history
  • Loading branch information
461 committed Apr 25, 2023
1 parent a319d1b commit 9dd9f85
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 58 deletions.
9 changes: 6 additions & 3 deletions packages/doc/config/utils/create-base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,15 +342,17 @@ function createSearchJson(mdPaths) {
const title = '`' + getTitleFromMd(mdstr) + '`'
const ast = mdAst.parse(mdstr).children
for (let j = 0; j < ast.length; j++) {
index++
const aa = ast[j]
const item: any = {
routePath: `${routePath}@${index}`,
title,
doc: aa,
belongMenu: findBelongMenu(routePath),
}
const mdType = aa.type
let mdType = aa.type
if (mdType === 'Header') {
mdType = `H${aa.depth}`
}
if (mdTypeMap[routePath][mdType] === undefined) {
mdTypeMap[routePath][mdType] = 0
} else {
Expand All @@ -360,6 +362,7 @@ function createSearchJson(mdPaths) {
item.mdTypeIndex = mdTypeMap[routePath][mdType]

result.push(item)
index++
}
}

Expand All @@ -372,7 +375,7 @@ function findBelongMenu(path) {
const items = menu[i]?.items || []
for (let j = 0; j < items.length; j++) {
if (items[j]?.path === path) {
return menu[i]
return items[j]
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions packages/doc/src/components/search/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,29 @@ export const LinkIcon = (props) => (
<path d="M13.414 17.586 18 22.172V8H8V6h10a2.002 2.002 0 0 1 2 2v14.172l4.586-4.586L26 19l-7 7-7-7Z"></path>
</svg>
)

export const LoadingIcon = (props) => {
return (
<svg {...props} viewBox="0 0 1024 1024" width="20" height="20">
<path
d="M876.864 782.592c3.264 0 6.272-3.2 6.272-6.656 0-3.456-3.008-6.592-6.272-6.592-3.264 0-6.272 3.2-6.272 6.592 0 3.456 3.008 6.656 6.272 6.656z m-140.544 153.344c2.304 2.432 5.568 3.84 8.768 3.84a12.16 12.16 0 0 0 8.832-3.84 13.76 13.76 0 0 0 0-18.56 12.224 12.224 0 0 0-8.832-3.84 12.16 12.16 0 0 0-8.768 3.84 13.696 13.696 0 0 0 0 18.56zM552.32 1018.24c3.456 3.648 8.32 5.76 13.184 5.76a18.368 18.368 0 0 0 13.184-5.76 20.608 20.608 0 0 0 0-27.968 18.368 18.368 0 0 0-13.184-5.824 18.368 18.368 0 0 0-13.184 5.76 20.608 20.608 0 0 0 0 28.032z m-198.336-5.76c4.608 4.8 11.072 7.68 17.6 7.68a24.448 24.448 0 0 0 17.536-7.68 27.456 27.456 0 0 0 0-37.248 24.448 24.448 0 0 0-17.536-7.68 24.448 24.448 0 0 0-17.6 7.68 27.52 27.52 0 0 0 0 37.184z m-175.68-91.84c5.76 6.08 13.824 9.6 21.952 9.6a30.592 30.592 0 0 0 22.016-9.6 34.368 34.368 0 0 0 0-46.592 30.592 30.592 0 0 0-22.016-9.6 30.592 30.592 0 0 0-21.952 9.6 34.368 34.368 0 0 0 0 46.592z m-121.152-159.36c6.912 7.36 16.64 11.648 26.368 11.648a36.736 36.736 0 0 0 26.432-11.584 41.28 41.28 0 0 0 0-55.936 36.736 36.736 0 0 0-26.432-11.584 36.8 36.8 0 0 0-26.368 11.52 41.28 41.28 0 0 0 0 56zM12.736 564.672a42.88 42.88 0 0 0 30.784 13.44 42.88 42.88 0 0 0 30.784-13.44 48.128 48.128 0 0 0 0-65.216 42.88 42.88 0 0 0-30.72-13.44 42.88 42.88 0 0 0-30.848 13.44 48.128 48.128 0 0 0 0 65.216z m39.808-195.392a48.96 48.96 0 0 0 35.2 15.36 48.96 48.96 0 0 0 35.2-15.36 54.976 54.976 0 0 0 0-74.56 48.96 48.96 0 0 0-35.2-15.424 48.96 48.96 0 0 0-35.2 15.424 54.976 54.976 0 0 0 0 74.56zM168.32 212.48c10.368 11.008 24.96 17.408 39.68 17.408 14.592 0 29.184-6.4 39.552-17.408a61.888 61.888 0 0 0 0-83.84 55.104 55.104 0 0 0-39.616-17.408c-14.656 0-29.248 6.4-39.616 17.408a61.888 61.888 0 0 0 0 83.84zM337.344 124.8c11.52 12.16 27.712 19.264 43.968 19.264 16.256 0 32.448-7.04 43.968-19.264a68.672 68.672 0 0 0 0-93.184 61.248 61.248 0 0 0-43.968-19.264 61.248 61.248 0 0 0-43.968 19.264 68.736 68.736 0 0 0 0 93.184z m189.632-1.088c12.672 13.44 30.528 21.248 48.448 21.248s35.712-7.808 48.384-21.248a75.584 75.584 0 0 0 0-102.464A67.392 67.392 0 0 0 575.36 0c-17.92 0-35.776 7.808-48.448 21.248a75.584 75.584 0 0 0 0 102.464z m173.824 86.592c13.824 14.592 33.28 23.104 52.736 23.104 19.584 0 39.04-8.512 52.8-23.104a82.432 82.432 0 0 0 0-111.744 73.472 73.472 0 0 0-52.8-23.168c-19.52 0-38.912 8.512-52.736 23.168a82.432 82.432 0 0 0 0 111.744z m124.032 158.528c14.976 15.872 36.032 25.088 57.216 25.088 21.12 0 42.24-9.216 57.152-25.088a89.344 89.344 0 0 0 0-121.088 79.616 79.616 0 0 0-57.152-25.088c-21.184 0-42.24 9.216-57.216 25.088a89.344 89.344 0 0 0 0 121.088z m50.432 204.032c16.128 17.088 38.784 27.008 61.632 27.008 22.784 0 45.44-9.92 61.568-27.008a96.256 96.256 0 0 0 0-130.432 85.76 85.76 0 0 0-61.568-27.072c-22.848 0-45.44 9.984-61.632 27.072a96.192 96.192 0 0 0 0 130.432z"
fill="#262626"
></path>
</svg>
)
}

export const EmptyIcon = () => (
<svg width="48" height="48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M24 5v6m7 1 4-4m-18 4-4-4m27 33H8a2 2 0 0 1-2-2v-8.46a2 2 0 0 1 .272-1.007l6.15-10.54A2 2 0 0 1 14.148 18H33.85a2 2 0 0 1 1.728.992l6.149 10.541A2 2 0 0 1 42 30.541V39a2 2 0 0 1-2 2Z"
stroke="currentColor"
strokeWidth="4"
></path>
<path
d="M41.5 30H28s-1 3-4 3-4-3-4-3H6.5"
stroke="currentColor"
strokeWidth="4"
></path>
</svg>
)
47 changes: 46 additions & 1 deletion packages/doc/src/components/search/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,46 @@
width: 560px;
border-radius: 16px;
transform: translate(-50%);

.loading-wrapper{
width: 100%;
padding: 30px;
display: flex;
align-items: center;
justify-content: center;

svg path {
fill: @primary-color !important;
}
svg {
animation: SEARCH_LOADING 1s infinite linear;
}
}

.empty-wrapper {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30px;
color: #4c5b6a;

.empty-tips {
margin-top: 12px;
span {
font-weight: 500;
}
}
}
}

@keyframes SEARCH_LOADING {
0% {
transform: rotateZ(0);
}
100% {
transform: rotateZ(360deg);
}
}

&-input {
Expand Down Expand Up @@ -97,11 +137,16 @@
max-height: 120px;
overflow: hidden;

.md-content-wrapper {
width: 100%;
height: 100%;
overflow: hidden;
}

.md-content {
padding: 6px 8px;
font-size: 14px;
line-height: 1.5;
width: 100%;
}

* {
Expand Down
178 changes: 127 additions & 51 deletions packages/doc/src/components/search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,120 @@
// @ts-ignore
import React, { useState, useEffect } from 'react'
import React, { useState, useEffect, useRef } from 'react'
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
import { routerEvent } from '../../utils/history'
import * as search from '../../utils/search'
import { SearchIcon, ClearIcon, PageIcon, LinkIcon } from './icons'
import { useDebounce } from '../../hooks/index'
import {
SearchIcon,
ClearIcon,
PageIcon,
LinkIcon,
LoadingIcon,
EmptyIcon,
} from './icons'
import './index.less'

const clsPre = 'antm-doc-search'
const mdTypeToTag = {
Table: 'table',
Paragraph: 'p',
CodeBlock: 'code',
H1: 'h1',
H2: 'h2',
H3: 'h3',
H4: 'h4',
H5: 'h5',
List: 'ul',
}
const clsPre = 'antm-doc-search'
const config = {
highlight: function (str, lang) {
return hljs.highlight(str, { language: lang || 'markdown' }).value
},
html: true,
}

const Markdown = MarkdownIt(config)
type Iprops = {
routeType?: 'hash' | 'history'
}

export default function Search(props: Iprops) {
const [show, setShow] = useState(true)
const [show, setShow] = useState(false)
const [result, setResult] = useState({})
const [words, setWords] = useState('')
const [loading, setLoading] = useState(false)
const [searchWords, setSearchWords] = useState('')
const inputRef = useRef<any>()

useEffect(() => {
requestIdleCallback(async () => {
await search.init()
const res = await search.run('规则')

res.map((item) => {
const menuName = item.belongMenu.name
if (!result[menuName]) {
result[menuName] = [item]
} else {
result[menuName].push(item)
}
})

setResult({ ...result })
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])

const handlePage = (path, docTarget) => {
setShow(false)
routerEvent.switch(path, props.routeType)
const mdType = docTarget.doc.type
let mdType = docTarget.doc.type
if (mdType === 'Header') {
mdType = `H${docTarget.doc.depth}`
}
const tagType = mdTypeToTag[mdType]
console.info(docTarget)

if (tagType) {
document
.querySelectorAll(`.antm-docs-markdown ${tagType}`)
[docTarget.mdTypeIndex]?.scrollIntoView()
const tt = document.querySelectorAll(`.antm-docs-markdown ${tagType}`)[
docTarget.mdTypeIndex
]

const rect = tt?.getClientRects() || []
window.scrollTo(0, rect[0]?.y ? rect[0]?.y - 50 : 0)
}
}

const handleSearch = useDebounce(
async (words) => {
setSearchWords(words)
if (!words) return
setLoading(true)
const res = await search.run(words)
const result = {}

res.map((item) => {
const menuName = item.belongMenu.title
if (!result[menuName]) {
result[menuName] = [item]
} else {
result[menuName].push(item)
}
})

setResult(result)
setTimeout(() => {
setLoading(false)
}, 700)
},
1000,
[words],
)

const handleInput = (e) => {
const ws = e.target.value
setWords(ws)
handleSearch(ws)
}

useEffect(() => {
if (!show) {
setWords('')
setSearchWords('')
}
if (show) {
inputRef.current?.focus()
}
}, [show])

return (
<>
<div className={`${clsPre}-btn-wrapper`}>
Expand All @@ -72,39 +126,61 @@ export default function Search(props: Iprops) {
<div className={`${clsPre}-modal`}>
<div className={`${clsPre}-input`}>
<SearchIcon className={`${clsPre}-btn`} />
<input placeholder="搜索文档" />
<ClearIcon />
<input
ref={inputRef}
placeholder="搜索文档"
value={words}
onChange={handleInput}
/>
<ClearIcon onClick={() => setWords('')} />
</div>

<div className={`${clsPre}-result`}>
{Object.keys(result).map((key, i) => {
const item = result[key]
return (
<div key={`seatch-result-rows${i}`}>
<div className="result-nav">{key}</div>
<div className="result-rows">
{item.map((it, index) => (
<div
className="row"
key={`seatch-result-item${index}@${i}`}
onClick={() => handlePage(it.routePath, it)}
>
<PageIcon />
<div className="md-content">
<div
dangerouslySetInnerHTML={{
__html: Markdown.render(it.doc.raw),
}}
></div>
{loading && (
<div className="loading-wrapper">
<LoadingIcon />
</div>
)}
{!loading && searchWords && Object.keys(result).length === 0 && (
<div className="empty-wrapper">
<EmptyIcon />
<div className="empty-tips">
未找到关于<span> &ldquo;{searchWords} &ldquo;</span>{' '}
的搜索结果
</div>
</div>
)}
{!!Object.keys(result).length && !loading && (
<div className={`${clsPre}-result`}>
{Object.keys(result).map((key, i) => {
const item = result[key]
return (
<div key={`seatch-result-rows${i}`}>
<div className="result-nav">{key}</div>
<div className="result-rows">
{item.map((it, index) => (
<div
className="row"
key={`seatch-result-item${index}@${i}`}
onClick={() => handlePage(it.routePath, it)}
>
<PageIcon />
<div className="md-content-wrapper">
<div className="md-content">
<div
dangerouslySetInnerHTML={{
__html: Markdown.render(it.doc.raw),
}}
></div>
</div>
</div>
<LinkIcon />
</div>
<LinkIcon />
</div>
))}
))}
</div>
</div>
</div>
)
})}
</div>
)
})}
</div>
)}
</div>
</>
)}
Expand Down
Loading

0 comments on commit 9dd9f85

Please # to comment.