This repository has been archived on 2022-08-14. You can view files and clone it, but cannot push or open issues or pull requests.
outline/frontend/scenes/Search/Search.js

130 lines
3.4 KiB
JavaScript
Raw Normal View History

2017-05-12 00:23:56 +00:00
// @flow
import React from 'react';
import ReactDOM from 'react-dom';
2016-07-18 03:59:32 +00:00
import { observer } from 'mobx-react';
2016-08-12 12:56:39 +00:00
import _ from 'lodash';
import Flex from 'components/Flex';
import { withRouter } from 'react-router';
import { searchUrl } from 'utils/routeHelpers';
import styled from 'styled-components';
import ArrowKeyNavigation from 'boundless-arrow-key-navigation';
2016-07-19 07:30:47 +00:00
2017-05-10 07:02:11 +00:00
import SearchField from './components/SearchField';
2016-07-19 07:30:47 +00:00
import SearchStore from './SearchStore';
2016-07-18 03:59:32 +00:00
2017-05-10 07:02:11 +00:00
import CenteredContent from 'components/CenteredContent';
import DocumentPreview from 'components/DocumentPreview';
import PageTitle from 'components/PageTitle';
2017-05-10 07:02:11 +00:00
2017-05-12 00:23:56 +00:00
type Props = {
history: Object,
match: Object,
2017-05-17 07:11:13 +00:00
notFound: ?boolean,
2017-05-12 00:23:56 +00:00
};
const Container = styled(CenteredContent)`
position: relative;
`;
const ResultsWrapper = styled(Flex)`
position: absolute;
2017-07-02 00:16:18 +00:00
transition: all 300ms cubic-bezier(0.65, 0.05, 0.36, 1);
top: ${props => (props.pinToTop ? '0%' : '50%')};
margin-top: ${props => (props.pinToTop ? '40px' : '-75px')};
width: 100%;
`;
2017-07-02 00:16:18 +00:00
const ResultList = styled(Flex)`
opacity: ${props => (props.visible ? '1' : '0')};
transition: all 400ms cubic-bezier(0.65, 0.05, 0.36, 1);
`;
@observer class Search extends React.Component {
firstDocument: HTMLElement;
2017-05-12 00:23:56 +00:00
props: Props;
store: SearchStore;
2016-07-19 07:30:47 +00:00
2017-05-12 00:23:56 +00:00
constructor(props: Props) {
2016-07-19 07:30:47 +00:00
super(props);
this.store = new SearchStore();
this.updateSearchResults();
2016-07-19 07:30:47 +00:00
}
componentDidUpdate(prevProps) {
if (prevProps.match.params.query !== this.props.match.params.query) {
this.updateSearchResults();
}
}
handleKeyDown = ev => {
// ESC
if (ev.which === 27) {
ev.preventDefault();
this.props.history.goBack();
}
// Down
if (ev.which === 40) {
ev.preventDefault();
if (this.firstDocument) {
const element = ReactDOM.findDOMNode(this.firstDocument);
// $FlowFixMe
if (element && element.focus) element.focus();
}
}
};
updateSearchResults = _.debounce(() => {
this.store.search(this.props.match.params.query);
}, 250);
updateQuery = query => {
this.props.history.replace(searchUrl(query));
};
setFirstDocumentRef = ref => {
this.firstDocument = ref;
};
2016-07-18 03:59:32 +00:00
render() {
const query = this.props.match.params.query;
const hasResults = this.store.documents.length > 0;
2016-07-19 07:30:47 +00:00
2016-07-18 03:59:32 +00:00
return (
<Container auto>
<PageTitle title="Search" />
{this.props.notFound &&
<div>
<h1>Not Found</h1>
<p>We're unable to find the page you're accessing.</p>
<hr />
</div>}
<ResultsWrapper pinToTop={hasResults} column auto>
<SearchField
searchTerm={this.store.searchTerm}
onKeyDown={this.handleKeyDown}
onChange={this.updateQuery}
value={query || ''}
/>
2017-07-02 00:16:18 +00:00
<ResultList visible={hasResults}>
<ArrowKeyNavigation
mode={ArrowKeyNavigation.mode.VERTICAL}
defaultActiveChildIndex={0}
>
{this.store.documents.map((document, index) => (
<DocumentPreview
innerRef={ref => index === 0 && this.setFirstDocumentRef(ref)}
key={document.id}
document={document}
highlight={this.store.searchTerm}
/>
))}
</ArrowKeyNavigation>
</ResultList>
</ResultsWrapper>
</Container>
2016-07-18 03:59:32 +00:00
);
}
}
export default withRouter(Search);