redoc/src/components/SearchBox/SearchBox.tsx

157 lines
3.7 KiB
TypeScript
Raw Normal View History

2018-02-08 19:41:02 +03:00
import * as React from 'react';
import { IMenuItem } from '../../services/MenuStore';
import { SearchStore } from '../../services/SearchStore';
import { MenuItem } from '../SideMenu/MenuItem';
2018-02-22 12:36:03 +03:00
2018-02-22 12:26:53 +03:00
import { MarkerService } from '../../services/MarkerService';
import { SearchDocument } from '../../services/SearchWorker.worker';
2018-02-08 19:41:02 +03:00
import { ClearIcon, SearchIcon, SearchInput, SearchResultsBox, SearchWrap } from './elements';
2018-02-08 19:41:02 +03:00
export interface SearchBoxProps {
search: SearchStore;
2018-02-22 12:26:53 +03:00
marker: MarkerService;
2018-02-08 19:41:02 +03:00
getItemById: (id: string) => IMenuItem | undefined;
onActivate: (item: IMenuItem) => void;
2018-02-22 19:48:50 +03:00
className?: string;
2018-02-08 19:41:02 +03:00
}
export interface SearchBoxState {
results: any;
term: string;
activeItemIdx: number;
2018-02-08 19:41:02 +03:00
}
2018-02-22 19:48:50 +03:00
interface SearchResult {
item: IMenuItem;
score: number;
}
2018-02-08 19:41:02 +03:00
export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> {
activeItemRef: MenuItem | null = null;
2018-02-08 19:41:02 +03:00
constructor(props) {
super(props);
this.state = {
results: [],
term: '',
activeItemIdx: -1,
2018-02-08 19:41:02 +03:00
};
}
2018-02-22 12:26:53 +03:00
clearResults(term: string) {
this.setState({
results: [],
term,
});
this.props.marker.unmark();
}
2018-02-22 12:36:03 +03:00
clear = () => {
2018-02-22 12:26:53 +03:00
this.setState({
results: [],
term: '',
activeItemIdx: -1,
2018-02-22 12:26:53 +03:00
});
this.props.marker.unmark();
2018-02-22 12:36:03 +03:00
};
2018-02-22 12:26:53 +03:00
handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.keyCode === 27) {
// ESQ
2018-02-22 12:26:53 +03:00
this.clear();
}
if (event.keyCode === 40) {
// Arrow down
this.setState({
activeItemIdx: Math.min(this.state.activeItemIdx + 1, this.state.results.length - 1),
});
event.preventDefault();
}
if (event.keyCode === 38) {
// Arrow up
this.setState({
activeItemIdx: Math.max(0, this.state.activeItemIdx - 1),
});
event.preventDefault();
}
if (event.keyCode === 13) {
// enter
const activeResult = this.state.results[this.state.activeItemIdx];
if (activeResult) {
const item = this.props.getItemById(activeResult.id);
if (item) {
this.props.onActivate(item);
}
}
}
2018-02-22 12:26:53 +03:00
};
setResults(results: SearchDocument[], term: string) {
this.setState({
results,
term,
});
this.props.marker.mark(term);
}
2018-02-08 19:41:02 +03:00
search = (event: React.ChangeEvent<HTMLInputElement>) => {
const q = event.target.value;
if (q.length < 3) {
2018-02-22 12:26:53 +03:00
this.clearResults(q);
2018-02-08 19:41:02 +03:00
return;
}
2018-02-22 12:26:53 +03:00
2018-02-08 19:41:02 +03:00
this.setState({
term: q,
});
this.props.search.search(event.target.value).then(res => {
2018-02-22 12:26:53 +03:00
this.setResults(res, q);
2018-02-08 19:41:02 +03:00
});
};
render() {
const { activeItemIdx } = this.state;
2018-02-22 19:48:50 +03:00
const results: SearchResult[] = this.state.results.map(res => ({
item: this.props.getItemById(res.id),
score: res.score,
}));
results.sort((a, b) => b.score - a.score);
2018-02-08 19:41:02 +03:00
return (
<SearchWrap role="search">
2018-02-22 12:36:03 +03:00
{this.state.term && <ClearIcon onClick={this.clear}>×</ClearIcon>}
2018-02-08 19:41:02 +03:00
<SearchIcon />
<SearchInput
value={this.state.term}
onKeyDown={this.handleKeyDown}
2018-02-08 19:41:02 +03:00
placeholder="Search..."
type="text"
onChange={this.search}
/>
2018-02-22 19:48:50 +03:00
{results.length > 0 && (
<SearchResultsBox data-role="search:results">
{results.map((res, idx) => (
2018-02-08 19:41:02 +03:00
<MenuItem
item={Object.create(res.item, {
active: {
value: idx === activeItemIdx,
},
})}
2018-02-08 19:41:02 +03:00
onActivate={this.props.onActivate}
withoutChildren={true}
2018-02-22 19:48:50 +03:00
key={res.item.id}
data-role="search:result"
2018-02-08 19:41:02 +03:00
/>
))}
</SearchResultsBox>
)}
</SearchWrap>
2018-02-08 19:41:02 +03:00
);
}
}