redoc/src/components/SearchBox/SearchBox.tsx

158 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';
2018-05-18 15:13:46 +03:00
import { SearchResult } from '../../services/SearchWorker.worker';
2018-02-08 19:41:02 +03:00
2018-05-16 12:44:36 +03:00
import {
ClearIcon,
SearchIcon,
SearchInput,
SearchResultsBox,
SearchWrap,
} from './styled.elements';
2018-02-08 19:41:02 +03:00
export interface SearchBoxProps {
2018-05-18 15:13:46 +03:00
search: SearchStore<string>;
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 {
2018-05-18 15:13:46 +03:00
results: SearchResult[];
2018-02-08 19:41:02 +03:00
term: string;
activeItemIdx: 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) {
2018-05-18 15:13:46 +03:00
const item = this.props.getItemById(activeResult.meta);
if (item) {
this.props.onActivate(item);
}
}
}
2018-02-22 12:26:53 +03:00
};
2018-05-18 15:13:46 +03:00
setResults(results: SearchResult[], term: string) {
2018-02-22 12:26:53 +03:00
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-05-18 15:13:46 +03:00
const results = this.state.results.map(res => ({
item: this.props.getItemById(res.meta)!,
2018-02-22 19:48:50 +03:00
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
);
}
}