/** @jsx h */ import { h, createRef, Component } from 'preact'; import { noop } from '../../lib/utils'; import Template from '../Template/Template'; import type { SearchBoxCSSClasses, SearchBoxTemplates, } from '../../widgets/search-box/search-box'; import type { ComponentCSSClasses } from '../../types'; export type SearchBoxComponentCSSClasses = ComponentCSSClasses; export type SearchBoxComponentTemplates = Required; type SearchBoxProps = { placeholder?: string; cssClasses: SearchBoxComponentCSSClasses; templates: SearchBoxComponentTemplates; query?: string; showSubmit?: boolean; showReset?: boolean; showLoadingIndicator?: boolean; refine?: (value: string) => void; autofocus?: boolean; searchAsYouType?: boolean; isSearchStalled?: boolean; disabled?: boolean; onChange?: (event: Event) => void; onSubmit?: (event: Event) => void; onReset?: (event: Event) => void; }; const defaultProps = { query: '', showSubmit: true, showReset: true, showLoadingIndicator: true, autofocus: false, searchAsYouType: true, isSearchStalled: false, disabled: false, onChange: noop, onSubmit: noop, onReset: noop, refine: noop, }; type SearchBoxPropsWithDefaultProps = SearchBoxProps & Readonly; type SearchBoxState = { query: string; focused: boolean; }; class SearchBox extends Component< SearchBoxPropsWithDefaultProps, SearchBoxState > { public static defaultProps = defaultProps; public state = { query: this.props.query, focused: false, }; private input = createRef(); /** * This public method is used in the RefinementList SFFV search box * to reset the input state when an item is selected. * * @see RefinementList#componentWillReceiveProps * @return {undefined} */ public resetInput() { this.setState({ query: '' }); } private onInput = (event: Event) => { const { searchAsYouType, refine, onChange } = this.props; const query = (event.target as HTMLInputElement).value; if (searchAsYouType) { refine(query); } this.setState({ query }); onChange(event); }; public componentWillReceiveProps(nextProps: SearchBoxPropsWithDefaultProps) { /** * when the user is typing, we don't want to replace the query typed * by the user (state.query) with the query exposed by the connector (props.query) * see: https://github.com/algolia/instantsearch.js/issues/4141 */ if (!this.state.focused && nextProps.query !== this.state.query) { this.setState({ query: nextProps.query }); } } private onSubmit = (event: Event) => { const { searchAsYouType, refine, onSubmit } = this.props; event.preventDefault(); event.stopPropagation(); if (this.input.current) { this.input.current.blur(); } if (!searchAsYouType) { refine(this.state.query); } onSubmit(event); return false; }; private onReset = (event: Event) => { const { refine, onReset } = this.props; const query = ''; if (this.input.current) { this.input.current.focus(); } refine(query); this.setState({ query }); onReset(event); }; private onBlur = () => { this.setState({ focused: false }); }; private onFocus = () => { this.setState({ focused: true }); }; public render() { const { cssClasses, placeholder, autofocus, showSubmit, showReset, showLoadingIndicator, templates, isSearchStalled, } = this.props; return (