import React, { Component, ReactNode, FocusEvent, FocusEventHandler, InputHTMLAttributes, ChangeEvent, ChangeEventHandler, } from 'react'; import classNames from '../../lib/classNames'; import withPlatform from '../../hoc/withPlatform'; import getClassname from '../../helpers/getClassName'; import Icon16SearchOutline from '@vkontakte/icons/dist/16/search_outline'; import Icon16Clear from '@vkontakte/icons/dist/16/clear'; import { IOS } from '../../lib/platform'; import { HasPlatform, HasRef } from '../../types'; import Touch, { TouchEventHandler, TouchEvent } from '../Touch/Touch'; import { VKUITouchEvent } from '../../lib/touch'; let searchId = 0; export type InputRef = (element: HTMLInputElement) => void; export interface SearchProps extends InputHTMLAttributes, HasRef, HasPlatform { /** * iOS only. Текст кнопки "отмена", которая чистит текстовое поле и убирает фокус. */ after?: ReactNode; icon?: ReactNode; onIconClick?: (e: VKUITouchEvent) => void; defaultValue?: string; } export interface SearchState { value?: string; focused?: boolean; } class Search extends Component { static defaultProps: SearchProps = { autoComplete: 'off', placeholder: 'Поиск', after: 'Отмена', }; isControlledOutside: boolean; inputEl: HTMLInputElement; searchId: string; constructor(props: SearchProps) { super(props); const state: SearchState = { focused: false, }; if (props.hasOwnProperty('value')) { this.isControlledOutside = true; } else { state.value = props.defaultValue || ''; } this.searchId = `search-${searchId++}`; this.state = state; } get value() { return this.isControlledOutside ? this.props.value : this.state.value; } onFocus: FocusEventHandler = (e: FocusEvent) => { this.setState({ focused: true }); this.props.onFocus && this.props.onFocus(e); }; onBlur: FocusEventHandler = (e: FocusEvent) => { this.setState({ focused: false }); this.props.onBlur && this.props.onBlur(e); }; onChange: ChangeEventHandler = (e?: ChangeEvent) => { const target = e.target as HTMLInputElement; if (!this.isControlledOutside) { this.setState({ value: target.value }); } if (this.props.onChange) { this.props.onChange(e); } }; onCancel: VoidFunction = () => { // eslint-disable-next-line @typescript-eslint/unbound-method const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; nativeInputValueSetter.call(this.inputEl, ''); const ev2 = new Event('input', { bubbles: true }); this.inputEl.dispatchEvent(ev2); }; onIconClickStart: TouchEventHandler = (e: TouchEvent) => { this.props.onIconClick && this.props.onIconClick(e.originalEvent); }; onIconCancelClickStart: TouchEventHandler = (e: TouchEvent) => { e.originalEvent.preventDefault(); this.inputEl.focus(); this.onCancel(); }; inputRef: InputRef = (element: HTMLInputElement) => { const getRef = this.props.getRef; this.inputEl = element; if (getRef) { if (typeof getRef === 'function') { getRef(element); } else { getRef.current = element; } } }; render() { const { className, onFocus, onBlur, onChange, defaultValue, value, placeholder, after, getRef, platform, icon, onIconClick, ...inputProps } = this.props; return (
{platform === IOS && after &&
{after}
}
{icon && {icon} } {!!this.value && }
{platform === IOS && after &&
{after}
}
); } } export default withPlatform(Search);