UNPKG

d2-ui

Version:
127 lines (110 loc) 4.58 kB
import React, { Component } from 'react'; import { findDOMNode } from 'react-dom'; import { Observable } from 'rxjs'; import log from 'loglevel'; import TextField from 'material-ui/TextField'; import IconButton from 'material-ui/IconButton'; import SvgIcon from '../../svg-icon/SvgIcon'; import ClearIcon from 'material-ui/svg-icons/content/clear'; import { white } from 'material-ui/styles/colors'; import { config } from 'd2/lib/d2'; import Notifications from '../notifications/Notifications'; import styles, { MENU_ITEM_WIDTH } from '../header-bar-styles'; import { search, handleKeyPress, setSearchFieldFocusTo, hideWhenNotHovering } from './search.stores'; import addD2Context from '../../component-helpers/addD2Context'; import withStateFrom from '../../component-helpers/withStateFrom'; import { searchStore$ } from './search.stores'; import SearchResults from './SearchResults'; config.i18n.strings.add('app_search_placeholder'); class SearchField extends Component { constructor(...args) { super(...args); this.state = { searchValue: '', }; this.setSearchValue = this.setSearchValue.bind(this); this.focusSearchField = this.focusSearchField.bind(this); this.onFocus = this.onFocus.bind(this); this.onBlur = this.onBlur.bind(this); this.clearSearchField = this.clearSearchField.bind(this); } componentDidMount() { const isCtrlPressed = event => event.ctrlKey; const isSpaceKey = event => event.keyCode === 32 || event.key === 'Space'; const combineFilters = (...args) => function combinedFiltersFn(event) { return args .map(filterFn => filterFn(event)) .every(filterResult => filterResult === true); }; // When Ctrl+Space is pressed focus the search field in the header bar this.disposable = Observable .fromEvent(window, 'keyup') // TODO: Using the window global directly is bad for testability .filter(combineFilters(isCtrlPressed, isSpaceKey)) .subscribe( this.focusSearchField, log.error, ); } componentWillUnmount() { if (this.disposable && this.disposable.unsubscribe) { this.disposable.unsubscribe(); } } render() { return ( <div style={styles.searchField}> <div style={styles.searchIconContainer}> <SvgIcon icon="Search" style={styles.searchIcon} /> </div> <div style={styles.searchFieldInnerWrap}> <TextField fullWidth value={this.props.searchValue || ''} onChange={this.setSearchValue} onFocus={this.onFocus} onBlur={this.onBlur} hintText={this.context.d2.i18n.getTranslation('app_search_placeholder')} hintStyle={styles.searchFieldHintText} inputStyle={styles.searchFieldInput} onKeyUp={this.onKeyUp} ref="searchBox" underlineFocusStyle={{ borderColor: white }} /> {this.props.searchValue ? <ClearIcon style={styles.clearIcon} color={white} onClick={this.clearSearchField} /> : ''} </div> <IconButton iconStyle={{ fill: 'white' }} onClick={this.focusSearchField}> <SvgIcon icon="Apps" /> </IconButton> <SearchResults /> </div> ); } focusSearchField() { const searchField = findDOMNode(this.refs.searchBox); if (searchField && searchField !== document.activeElement) { searchField.querySelector('input').focus(); } } clearSearchField() { if (this.state.hasFocus) { this.focusSearchField(); } search(''); } setSearchValue(event) { this.setState({ hasValue: Boolean(event.target.value) }); search(event.target.value); } onFocus() { this.setState({ hasFocus: true }); setSearchFieldFocusTo(true); } onBlur() { this.setState({ hasFocus: false }); hideWhenNotHovering(); } onKeyUp(event) { handleKeyPress(event, Math.floor(event.currentTarget.clientWidth / MENU_ITEM_WIDTH)); } } export default withStateFrom(searchStore$, addD2Context(SearchField));