amis/examples/components/DocSearch.jsx

199 lines
4.8 KiB
React
Raw Normal View History

2020-08-20 23:56:28 +08:00
/**
* @file 实现前端文档搜索
*/
2019-06-11 17:00:02 +08:00
import React from 'react';
2020-08-21 17:33:31 +08:00
import axios from 'axios';
2022-06-01 15:06:00 +08:00
import {Icon, Drawer, SearchBox} from 'amis';
2019-06-04 16:24:09 +08:00
2020-08-21 15:07:13 +08:00
let ContextPath = '';
if (process.env.NODE_ENV === 'production') {
ContextPath = '/amis';
}
2019-06-04 16:24:09 +08:00
export default class DocSearch extends React.Component {
2020-08-21 11:26:54 +08:00
docs = [];
2020-08-21 17:33:31 +08:00
ref = React.createRef();
2020-08-20 23:56:28 +08:00
constructor(props) {
super(props);
this.state = {
2020-08-21 17:33:31 +08:00
query: '',
show: false,
2020-08-20 23:56:28 +08:00
searchResults: [],
loadError: false
};
this.onSearch = this.onSearch.bind(this);
2020-08-21 17:33:31 +08:00
this.onChange = this.onChange.bind(this);
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.onEntered = this.onEntered.bind(this);
this.clearValue = this.clearValue.bind(this);
2020-08-20 23:56:28 +08:00
}
2020-08-21 17:33:31 +08:00
2019-11-07 10:41:14 +08:00
componentDidMount() {
2020-08-21 17:33:31 +08:00
axios
.get(ContextPath + __uri('../docs.json'))
2020-08-20 23:56:28 +08:00
.then(result => {
this.docs = result.data.docs;
})
.catch(err => {
this.setState({loadError: true});
});
}
2020-08-21 17:33:31 +08:00
onSearch() {
let query = this.state.query.trim().toLowerCase();
2020-08-20 23:56:28 +08:00
if (query === '') {
this.setState({searchResults: []});
return;
}
2020-08-21 17:33:31 +08:00
2020-08-20 23:56:28 +08:00
let results = [];
for (let doc of this.docs) {
2021-02-02 15:29:06 +08:00
let bodyTest = new RegExp(
query.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&'),
'i'
);
const match = bodyTest.exec(doc.body);
2020-08-20 23:56:28 +08:00
if (match) {
// 找最近一个 title 作为 hash
const beforeLines = doc.body
.substring(0, match.index)
.split('\n')
.reverse();
let hash = '';
for (const line of beforeLines) {
if (line.startsWith('##')) {
hash =
'#' +
line
.trim()
.replace(/#/g, '')
.trim()
.toLowerCase()
.replace(/ /g, '-');
break;
}
}
2020-08-20 23:56:28 +08:00
results.push({
title: doc.title,
path: doc.path + hash,
2020-08-20 23:56:28 +08:00
abstract: doc.body
.substring(Math.max(0, match.index - 20), match.index + 60)
.replace(
bodyTest,
`<strong>${doc.body.substring(
match.index,
match.index + query.length
)}</strong>`
)
2020-08-20 23:56:28 +08:00
});
} else if (doc.title.toLowerCase().indexOf(query) !== -1) {
results.push({
title: doc.title,
path: doc.path,
abstract: ''
});
} else if (doc.path.toLowerCase().indexOf(query) !== -1) {
2020-08-20 23:56:28 +08:00
results.push({
title: doc.title,
path: doc.path,
abstract: ''
});
}
}
this.setState({searchResults: results});
}
2020-08-21 17:33:31 +08:00
onChange(e) {
this.setState(
{
query: e.currentTarget.value
},
() => this.onSearch()
);
}
onOpen() {
this.setState({
show: true
});
}
onClose() {
this.setState({
show: false
});
}
onEntered() {
this.ref.current.focus();
}
clearValue() {
this.setState(
{
query: ''
},
() => {
this.setState({searchResults: []});
}
);
}
2019-11-07 10:41:14 +08:00
render() {
2020-08-20 23:56:28 +08:00
const searchResults = this.state.searchResults;
2020-08-21 17:33:31 +08:00
const ns = this.props.theme.ns;
2020-08-20 23:56:28 +08:00
return (
2020-08-21 17:33:31 +08:00
<>
2020-11-30 16:53:04 +08:00
{/* <div className={`${ns}TextControl-input Doc-search`}>
2020-08-21 17:33:31 +08:00
<Icon icon="search" className="icon" />
<input readOnly placeholder={'搜索...'} onClick={this.onOpen} />
2020-11-30 16:53:04 +08:00
</div> */}
<a onClick={this.onOpen}>
<Icon icon="search" className="icon" />
</a>
2020-08-21 17:33:31 +08:00
<Drawer
className="Doc-searchDrawer"
overlay
closeOnOutside
onHide={this.onClose}
onEntered={this.onEntered}
show={this.state.show}
position={'right'}
>
<div className={`${this.props.theme.ns}TextControl-input search-bar`}>
<Icon icon="search" className="icon" />
<input
ref={this.ref}
placeholder={'搜索...'}
onChange={this.onChange}
value={this.state.query}
/>
{this.state.query ? (
<a onClick={this.clearValue} className={`${ns}TextControl-clear`}>
<Icon icon="close" className="icon" />
</a>
) : null}
2020-08-21 12:54:47 +08:00
</div>
2020-08-21 17:33:31 +08:00
{searchResults.length > 0 ? (
<div className="search-result">
{searchResults.map(item => {
return (
<a href={ContextPath + item.path} key={`list_${item.path}`}>
<h4>{item.title}</h4>
<p dangerouslySetInnerHTML={{__html: item.abstract}} />
</a>
);
})}
</div>
) : null}
</Drawer>
</>
2020-08-20 23:56:28 +08:00
);
2019-11-07 10:41:14 +08:00
}
}