Skip to content

Instantly share code, notes, and snippets.

@russll

russll/search.js Secret

Last active March 31, 2017 11:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save russll/bee1d08b38aad1987e081a932ac7165b to your computer and use it in GitHub Desktop.
Save russll/bee1d08b38aad1987e081a932ac7165b to your computer and use it in GitHub Desktop.
Компонент поиска
import './search.css';
import classnames from 'classnames';
import Autosuggest from 'react-autosuggest';
import React, { Component, PropTypes } from 'react';
import { connectToStores } from 'fluxible-addons-react';
import SearchStore from '../../stores/search';
import { navigateAction } from 'fluxible-router';
import setRedirectUrl from '../../actions/setRedirectUrl';
import FindNothing from '../findNothing/findNothing';
import { setSearchMode, search } from '../../actions/search';
import { sendEvent, sendSearchQuestionShowEvent } from '../../actions/analytics';
import plural from '../../helpers/plural';
import {
createSearchNotFountEventData,
createAsk1StepShowEventData,
createPlaceShowSearchSuggestEventData,
createTagShowSearchSuggestEventData,
createSearchEventData
} from '../../helpers/analytics';
import { isAuthorized } from '../../helpers/userVerifications';
import { placesExtensions } from '../../helpers/suggestionFormatter';
import { getUrlWithProject, getUrlToEntity } from '../../helpers/url';
@connectToStores([SearchStore], context => ({ searchResult: context.getStore(SearchStore).getResult() }))
class Search extends Component {
static contextTypes = {
executeAction: PropTypes.func,
getStore: PropTypes.func
};
static propTypes = {
user: PropTypes.object.isRequired,
project: PropTypes.object.isRequired,
isFocused: PropTypes.bool.isRequired,
searchResult: PropTypes.array
};
static defaultProps = {
searchResult: []
};
state = {
value: ''
};
_searchQueryCompleted = false;
_searchEventSended = false;
_searchNotFound = false;
componentDidUpdate(prevProps, prevState) {
const isSearchNotFound = prevState.value != this.state.value &&
this.state.value.length > 3 &&
this.props.searchResult.length == 0 &&
!this._searchNotFound;
if (isSearchNotFound) {
this._searchNotFound = true;
this.context.executeAction(sendEvent, createSearchNotFountEventData({value: this.state.value}));
}
}
handlerSuggestionSelected = (event, { suggestion }) => {
const { value } = this.state;
const { project, user } = this.props;
const { type } = suggestion;
const url = type == 'ask' ?
getUrlToEntity('/ask' + (value ? `?question=${value}` : ''), project.code) :
getUrlToEntity(suggestion, project.code);
if (type == 'ask' && !isAuthorized(user)) {
const signinPath = getUrlWithProject('/signin', project.code);
this.context.executeAction(setRedirectUrl, url);
this.context.executeAction(navigateAction, {
url: signinPath
});
} else {
if (type == 'ask') {
this.context.executeAction(sendEvent, createAsk1StepShowEventData({place: 'search/suggest'}));
} else {
if (suggestion.type == 'questions') {
this.context.executeAction(sendSearchQuestionShowEvent, {suggestion, project});
}
if (suggestion.type == 'places') {
this.context.executeAction(sendEvent, createPlaceShowSearchSuggestEventData({suggestion}));
}
if (suggestion.type == 'tags') {
this.context.executeAction(sendEvent, createTagShowSearchSuggestEventData({suggestion}));
}
}
this.context.executeAction(navigateAction, { url });
}
this.context.executeAction(setSearchMode, false);
this.setState({value: ''});
};
handlerSearch = query => {
this._searchQueryCompleted = false;
if (!this._searchEventSended) {
this._searchEventSended = true;
this.context.executeAction(sendEvent, createSearchEventData());
}
context.executeAction(search, query)
.then(() => {
this._searchQueryCompleted = true;
})
.catch(() => {
this._searchQueryCompleted = true;
});
};
handlerInputChange = (event, { newValue }) => {
this.setState({
value: newValue
});
this._searchQueryCompleted = false;
this.handlerSearch(newValue);
};
handlerInputBlur = () => {
this.context.executeAction(setSearchMode, false);
};
getLabel(item) {
if (item.type == 'places') {
return placesExtensions[item.placeType];
}
if (item.type == 'tags') {
return 'тема';
}
return null;
}
getSuggestionValue = () => {
return this.state.value;
};
getSectionSuggestions(section) {
return section.suggestions;
}
shouldRenderSuggestions(value) {
return value.trim().length > 2;
}
addAskSuggestion(suggestions, query) {
return [...suggestions, {
type: 'ask',
title: `Задать вопрос «${query}»`,
highlighted: true
}];
}
renderSectionTitle(section) {
if (!section.isEmpty || !this._searchQueryCompleted) {
return null;
}
return (
<div className='search__results-section-title'>
<FindNothing
title={'Увы, ничего не нашлось'}
desc={'Попробуйте поискать другие темы или задайте вопрос'} />
</div>
);
}
renderSuggestion = suggestion => {
const {
title = '',
type,
answersCount = 0,
followersCount = 0,
questionsCount = 0,
places = [],
highlighted = false
} = suggestion;
const label = this.getLabel(suggestion);
const showFollowers = type != 'questions' && followersCount > 0;
const showAnswers = answersCount > 0;
const showquestionsCount = questionsCount > 0;
const showPlaces = places.length > 0;
return (
<div className={classnames('search__suggestions-item-link', { _highlighted: highlighted })}>
<span className="search__suggestions-item-title">{title}</span>
{label &&
<span className="search__suggestions-item-meta _label">
{label}
</span>
}
{showquestionsCount &&
<span className="search__suggestions-item-meta _questions">
{questionsCount}&nbsp;
{plural(questionsCount, ['вопрос', 'вопроса', 'вопросов'])}
</span>
}
{showFollowers &&
<span className="search__suggestions-item-meta _followers">
{followersCount}&nbsp;
{plural(followersCount, ['подписчик', 'подписчика', 'подписчиков'])}
</span>
}
{showAnswers &&
<span className="search__suggestions-item-meta _answers">
{answersCount}&nbsp;
{plural(answersCount, ['ответ', 'ответа', 'ответов'])}
</span>
}
{showPlaces && places[0].title &&
<span className="search__suggestions-item-meta _place">
{places[0].title}
</span>
}
</div>
);
};
render() {
const { searchResult } = this.props;
const { value } = this.state;
const placeholder = 'Что вы хотите найти?';
const suggestions = [{
isEmpty: searchResult.length == 0,
suggestions: this.addAskSuggestion(searchResult, value)
}];
return <Autosuggest
multiSection={true}
suggestions={suggestions}
getSectionSuggestions={this.getSectionSuggestions}
getSuggestionValue={this.getSuggestionValue}
renderSuggestion={this.renderSuggestion}
renderSectionTitle={::this.renderSectionTitle}
shouldRenderSuggestions={this.shouldRenderSuggestions}
focusInputOnSuggestionClick={false}
onSuggestionSelected={this.handlerSuggestionSelected}
inputProps={{
value,
placeholder,
onChange: this.handlerInputChange,
onBlur: this.handlerInputBlur
}}
theme={{
container: 'search',
input: 'search__input',
suggestionsContainer: 'search__results',
sectionContainer: 'search__results-section',
sectionSuggestionsContainer: 'search__suggestions',
suggestion: 'search__suggestions-item',
suggestionFocused: '_focused'
}} />;
}
}
export default Search;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment