123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- import {createRef, PureComponent} from 'react';
- import styled from '@emotion/styled';
- import classNames from 'classnames';
- import Button from 'sentry/components/button';
- import Input, {InputProps} from 'sentry/components/forms/controls/input';
- import {IconSearch} from 'sentry/icons';
- import {IconClose} from 'sentry/icons/iconClose';
- import {t} from 'sentry/locale';
- import {callIfFunction} from 'sentry/utils/callIfFunction';
- interface SearchBarProps extends Omit<InputProps, 'onChange'> {
- defaultQuery: string;
- onSearch: (query: string) => void;
- query: string;
- onChange?: (query: string) => void;
- width?: string;
- }
- type State = {
- dropdownVisible: boolean;
- query: string;
- };
- class SearchBar extends PureComponent<SearchBarProps, State> {
- static defaultProps: Pick<SearchBarProps, 'query' | 'defaultQuery' | 'onSearch'> = {
- query: '',
- defaultQuery: '',
- onSearch: function () {},
- };
- state: State = {
- query: this.props.query || this.props.defaultQuery,
- dropdownVisible: false,
- };
- UNSAFE_componentWillReceiveProps(nextProps: SearchBarProps) {
- if (nextProps.query !== this.props.query) {
- this.setState({
- query: nextProps.query,
- });
- }
- }
- searchInputRef = createRef<HTMLInputElement>();
- blur = () => {
- if (this.searchInputRef.current) {
- this.searchInputRef.current.blur();
- }
- };
- onSubmit = (evt: React.FormEvent<HTMLFormElement>) => {
- evt.preventDefault();
- this.blur();
- this.props.onSearch(this.state.query);
- };
- clearSearch = () => {
- this.setState({query: this.props.defaultQuery}, () => {
- this.props.onSearch(this.state.query);
- callIfFunction(this.props.onChange, this.state.query);
- });
- };
- onQueryFocus = () => {
- this.setState({
- dropdownVisible: true,
- });
- };
- onQueryBlur = () => {
- this.setState({dropdownVisible: false});
- };
- onQueryChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
- const {value} = evt.target;
- this.setState({query: value});
- callIfFunction(this.props.onChange, value);
- };
- render() {
- // Remove keys that should not be passed into Input
- const {
- className,
- width,
- query: _q,
- defaultQuery,
- onChange: _oC,
- onSearch: _oS,
- ...inputProps
- } = this.props;
- return (
- <div className={classNames('search', className)}>
- <form className="form-horizontal" onSubmit={this.onSubmit}>
- <div>
- <StyledInput
- {...inputProps}
- type="text"
- className="search-input"
- name="query"
- ref={this.searchInputRef}
- autoComplete="off"
- value={this.state.query}
- onBlur={this.onQueryBlur}
- onChange={this.onQueryChange}
- width={width}
- />
- <StyledIconSearch className="search-input-icon" size="sm" color="gray300" />
- {this.state.query !== defaultQuery && (
- <SearchClearButton
- type="button"
- className="search-clear-form"
- priority="link"
- onClick={this.clearSearch}
- size="xsmall"
- icon={<IconClose />}
- aria-label={t('Clear')}
- />
- )}
- </div>
- </form>
- </div>
- );
- }
- }
- const StyledInput = styled(Input)`
- width: ${p => (p.width ? p.width : undefined)};
- &.focus-visible {
- box-shadow: 0 0 0 1px ${p => p.theme.focusBorder};
- border-color: ${p => p.theme.focusBorder};
- outline: none;
- }
- `;
- const StyledIconSearch = styled(IconSearch)`
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- left: 14px;
- `;
- const SearchClearButton = styled(Button)`
- position: absolute;
- top: 50%;
- height: 16px;
- transform: translateY(-50%);
- right: 10px;
- font-size: ${p => p.theme.fontSizeLarge};
- color: ${p => p.theme.gray200};
- &:hover {
- color: ${p => p.theme.gray300};
- }
- `;
- export default SearchBar;
|