index.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import React, { PureComponent } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { Input, Icon, AutoComplete } from 'antd';
  4. import classNames from 'classnames';
  5. import Debounce from 'lodash-decorators/debounce';
  6. import Bind from 'lodash-decorators/bind';
  7. import styles from './index.less';
  8. export default class HeaderSearch extends PureComponent {
  9. static propTypes = {
  10. className: PropTypes.string,
  11. placeholder: PropTypes.string,
  12. onSearch: PropTypes.func,
  13. onPressEnter: PropTypes.func,
  14. defaultActiveFirstOption: PropTypes.bool,
  15. dataSource: PropTypes.array,
  16. defaultOpen: PropTypes.bool,
  17. onVisibleChange: PropTypes.func,
  18. };
  19. static defaultProps = {
  20. defaultActiveFirstOption: false,
  21. onPressEnter: () => {},
  22. onSearch: () => {},
  23. className: '',
  24. placeholder: '',
  25. dataSource: [],
  26. defaultOpen: false,
  27. onVisibleChange: () => {},
  28. };
  29. static getDerivedStateFromProps(props) {
  30. if ('open' in props) {
  31. return {
  32. searchMode: props.open,
  33. };
  34. }
  35. return null;
  36. }
  37. constructor(props) {
  38. super(props);
  39. this.state = {
  40. searchMode: props.defaultOpen,
  41. value: '',
  42. };
  43. }
  44. componentWillUnmount() {
  45. clearTimeout(this.timeout);
  46. }
  47. onKeyDown = e => {
  48. if (e.key === 'Enter') {
  49. const { onPressEnter } = this.props;
  50. const { value } = this.state;
  51. this.timeout = setTimeout(() => {
  52. onPressEnter(value); // Fix duplicate onPressEnter
  53. }, 0);
  54. }
  55. };
  56. onChange = value => {
  57. const { onChange } = this.props;
  58. this.setState({ value });
  59. if (onChange) {
  60. onChange(value);
  61. }
  62. };
  63. enterSearchMode = () => {
  64. const { onVisibleChange } = this.props;
  65. onVisibleChange(true);
  66. this.setState({ searchMode: true }, () => {
  67. const { searchMode } = this.state;
  68. if (searchMode) {
  69. this.input.focus();
  70. }
  71. });
  72. };
  73. leaveSearchMode = () => {
  74. this.setState({
  75. searchMode: false,
  76. value: '',
  77. });
  78. };
  79. // NOTE: 不能小于500,如果长按某键,第一次触发auto repeat的间隔是500ms,小于500会导致触发2次
  80. @Bind()
  81. @Debounce(500, {
  82. leading: true,
  83. trailing: false,
  84. })
  85. debouncePressEnter() {
  86. const { onPressEnter } = this.props;
  87. const { value } = this.state;
  88. onPressEnter(value);
  89. }
  90. render() {
  91. const { className, placeholder, open, ...restProps } = this.props;
  92. const { searchMode, value } = this.state;
  93. delete restProps.defaultOpen; // for rc-select not affected
  94. const inputClass = classNames(styles.input, {
  95. [styles.show]: searchMode,
  96. });
  97. return (
  98. <span
  99. className={classNames(className, styles.headerSearch)}
  100. onClick={this.enterSearchMode}
  101. onTransitionEnd={({ propertyName }) => {
  102. if (propertyName === 'width' && !searchMode) {
  103. const { onVisibleChange } = this.props;
  104. onVisibleChange(searchMode);
  105. }
  106. }}
  107. >
  108. <Icon type="search" key="Icon" />
  109. <AutoComplete
  110. key="AutoComplete"
  111. {...restProps}
  112. className={inputClass}
  113. value={value}
  114. onChange={this.onChange}
  115. getPopupContainer={triggerNode => triggerNode.parentNode}
  116. >
  117. <Input
  118. ref={node => {
  119. this.input = node;
  120. }}
  121. aria-label={placeholder}
  122. placeholder={placeholder}
  123. onKeyDown={this.onKeyDown}
  124. onBlur={this.leaveSearchMode}
  125. />
  126. </AutoComplete>
  127. </span>
  128. );
  129. }
  130. }