Files
protocol/packages/website/ts/components/docs/search/autocomplete.tsx
2019-08-23 23:55:20 +02:00

247 lines
7.2 KiB
TypeScript

import React from 'react';
import styled from 'styled-components';
// import Autocomplete from 'react-autocomplete';
import Autosuggest from 'react-autosuggest';
import { Highlight, Snippet } from 'react-instantsearch-dom';
import { colors } from 'ts/style/colors';
interface Props {
isHome?: boolean;
}
interface ToolsHit {
objectID: string;
title: string;
}
interface AutoCompleteProps {
isHome?: boolean;
hits: object[];
currentRefinement: string;
refine?: (value: string) => void;
}
interface AutoCompleteState {
value: string;
}
interface HitProps {
[key: string]: any;
}
interface AutosuggestThemeClasses {
[key: string]: any;
}
const theme: AutosuggestThemeClasses = {
container: 'react-autosuggest__container',
containerOpen: 'react-autosuggest__container--open',
input: 'react-autosuggest__input',
inputOpen: 'react-autosuggest__input--open',
inputFocused: 'react-autosuggest__input--focused',
suggestionsContainer: 'react-autosuggest__suggestions-container',
suggestionsContainerOpen: 'react-autosuggest__suggestions-container--open',
suggestionsList: 'react-autosuggest__suggestions-list',
suggestion: 'react-autosuggest__suggestion',
suggestionFirst: 'react-autosuggest__suggestion--first',
suggestionHighlighted: 'react-autosuggest__suggestion--highlighted',
sectionContainer: 'react-autosuggest__section-container',
sectionContainerFirst: 'react-autosuggest__section-container--first',
sectionTitle: 'react-autosuggest__section-title',
};
export class CustomAutoComplete extends React.Component<AutoCompleteProps, AutoCompleteState> {
public static defaultProps: AutoCompleteProps = {
isHome: false,
hits: [],
currentRefinement: '',
};
constructor(props: AutoCompleteProps) {
super(props);
this.state = {
value: '',
};
}
public render(): React.ReactNode {
const { hits, isHome } = this.props;
const { value } = this.state;
const inputProps = {
placeholder: 'Search docs',
onChange: this._onChange.bind(this),
value,
};
return (
<Wrapper isHome={isHome}>
<Autosuggest
highlightFirstSuggestion={true}
theme={theme}
suggestions={hits}
multiSection={true}
onSuggestionSelected={this._onSuggestionSelected.bind(this)}
onSuggestionsFetchRequested={this._onSuggestionsFetchRequested.bind(this)}
onSuggestionHighlighted={this._onSuggestionHighlighted.bind(this)}
onSuggestionsClearRequested={this._onSuggestionsClearRequested.bind(this)}
getSuggestionValue={this._getSuggestionValue.bind(this)}
renderSuggestion={this._renderSuggestion.bind(this)}
inputProps={inputProps}
renderSectionTitle={this._renderSectionTitle.bind(this)}
getSectionSuggestions={this._getSectionSuggestions.bind(this)}
/>
</Wrapper>
);
}
private _onChange(event: HitProps, { newValue }: HitProps): void {
this.setState({
value: newValue,
});
}
private _onSuggestionsFetchRequested({ value }: HitProps): void {
this.props.refine(value);
}
private _onSuggestionsClearRequested(): void {
this.props.refine('');
}
// tslint:disable-next-line: no-empty
private _onSuggestionHighlighted(): void {}
private _getSuggestionValue(hit: HitProps): string {
return hit.textContent;
}
private _onSuggestionSelected(event: HitProps, { suggestion }: HitProps): void {
// tslint:disable-next-line: no-console
console.log(suggestion);
}
private _renderSuggestion(hit: HitProps): React.ReactNode {
return (
<HitWrapper>
<Highlight attribute="title" hit={hit} tagName="mark" />
<Snippet attribute="textContent" hit={hit} tagName="mark" />
</HitWrapper>
);
}
private _renderSectionTitle(section: HitProps): React.ReactNode {
const titles: { [key: string]: any } = {
'0x_tools_test': 'Tools',
'0x_guides_test': 'Guides',
};
return <SectionTitle>{titles[section.index]}</SectionTitle>;
}
private _getSectionSuggestions(section: HitProps): string {
return section.hits;
}
}
const SectionTitle = styled.p`
color: ${colors.brandDark};
background-color: rgba(0, 56, 49, 0.1);
border-radius: 4px;
font-size: 12px;
padding: 7px 10px 5px;
text-transform: uppercase;
position: absolute;
margin: 30px;
`;
const HitWrapper = styled.div`
display: flex;
align-items: flex-start;
`;
const Wrapper = styled.div<Props>`
position: relative;
.react-autosuggest__container {
padding: 13px 30px;
border: 1px solid transparent;
&--open {
background-color: ${colors.white};
border-color: #dbdfdd;
}
}
.react-autosuggest__section-container {
display: flex;
}
.react-autosuggest__suggestions-container {
position: absolute;
right: 0;
background-color: ${colors.white};
z-index: 10;
min-width: 420px;
width: 100%;
flex-grow: 1;
&--open {
border: 1px solid #dbdfdd;
border-top: none;
}
}
.react-autosuggest__suggestions-list {
flex-grow: 1;
}
.react-autosuggest__input {
background: url("data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill='%23fff' fill-opacity='.01' d='M0 0h24v24H0z'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M5 10.5a5.5 5.5 0 1 1 11 0 5.5 5.5 0 0 1-11 0zM10.5 3a7.5 7.5 0 1 0 4.55 13.463l4.743 4.744 1.414-1.414-4.744-4.744A7.5 7.5 0 0 0 10.5 3z' fill='%235C5C5C'/%3E%3C/svg%3E")
transparent left center no-repeat;
font-size: 22px;
padding: 18px 18px 21px 35px;
width: 100%;
border: 0;
border-bottom: 1px solid #b4bebd;
outline: none;
${({ isHome }) =>
!isHome &&
`
background-color: #EBEEEC;
border-bottom: 0;
padding: 13px 21px 15px 52px;
background-position: left 21px center;
font-size: 1rem;
`};
&--open {
border-bottom-color: ${colors.brandLight};
}
&:before {
content: '';
width: 30px;
height: 30px;
opacity: 0.15;
position: absolute;
top: 0;
left: 0;
}
}
.react-autosuggest__suggestion {
color: ${colors.brandDark};
font-size: 0.85rem;
line-height: 1.4;
font-weight: 400;
padding: 25px 30px 25px 178px;
min-height: 110px;
border-bottom: 1px solid #eee;
transition: background-color 300ms ease-in-out;
&--highlighted {
background-color: ${colors.backgroundLight};
}
}
`;