index.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import { AutoComplete, Icon, Input } from 'antd';
  2. import { AutoCompleteProps, DataSourceItemType } from 'antd/es/auto-complete';
  3. import React, { Component } from 'react';
  4. import classNames from 'classnames';
  5. import debounce from 'lodash/debounce';
  6. import styles from './index.less';
  7. export interface HeaderSearchProps {
  8. onPressEnter: (value: string) => void;
  9. onSearch: (value: string) => void;
  10. onChange: (value: string) => void;
  11. onVisibleChange: (b: boolean) => void;
  12. className: string;
  13. placeholder: string;
  14. defaultActiveFirstOption: boolean;
  15. dataSource: DataSourceItemType[];
  16. defaultOpen: boolean;
  17. open?: boolean;
  18. }
  19. interface HeaderSearchState {
  20. value: string;
  21. searchMode: boolean;
  22. }
  23. export default class HeaderSearch extends Component<HeaderSearchProps, HeaderSearchState> {
  24. static defaultProps = {
  25. defaultActiveFirstOption: false,
  26. onPressEnter: () => {},
  27. onSearch: () => {},
  28. onChange: () => {},
  29. className: '',
  30. placeholder: '',
  31. dataSource: [],
  32. defaultOpen: false,
  33. onVisibleChange: () => {},
  34. };
  35. static getDerivedStateFromProps(props: HeaderSearchProps) {
  36. if ('open' in props) {
  37. return {
  38. searchMode: props.open,
  39. };
  40. }
  41. return null;
  42. }
  43. private timeout: number | undefined = undefined;
  44. private inputRef: Input | null = null;
  45. constructor(props: HeaderSearchProps) {
  46. super(props);
  47. this.state = {
  48. searchMode: props.defaultOpen,
  49. value: '',
  50. };
  51. this.debouncePressEnter = debounce(this.debouncePressEnter, 500, {
  52. leading: true,
  53. trailing: false,
  54. });
  55. }
  56. componentWillUnmount() {
  57. clearTimeout(this.timeout);
  58. }
  59. onKeyDown = (e: React.KeyboardEvent) => {
  60. if (e.key === 'Enter') {
  61. const { onPressEnter } = this.props;
  62. const { value } = this.state;
  63. this.timeout = window.setTimeout(() => {
  64. onPressEnter(value); // Fix duplicate onPressEnter
  65. }, 0);
  66. }
  67. };
  68. onChange: AutoCompleteProps['onChange'] = value => {
  69. if (typeof value === 'string') {
  70. const { onSearch, onChange } = this.props;
  71. this.setState({ value });
  72. if (onSearch) {
  73. onSearch(value);
  74. }
  75. if (onChange) {
  76. onChange(value);
  77. }
  78. }
  79. };
  80. enterSearchMode = () => {
  81. const { onVisibleChange } = this.props;
  82. onVisibleChange(true);
  83. this.setState({ searchMode: true }, () => {
  84. const { searchMode } = this.state;
  85. if (searchMode && this.inputRef) {
  86. this.inputRef.focus();
  87. }
  88. });
  89. };
  90. leaveSearchMode = () => {
  91. this.setState({
  92. searchMode: false,
  93. value: '',
  94. });
  95. };
  96. debouncePressEnter = () => {
  97. const { onPressEnter } = this.props;
  98. const { value } = this.state;
  99. onPressEnter(value);
  100. };
  101. render() {
  102. const { className, placeholder, open, ...restProps } = this.props;
  103. const { searchMode, value } = this.state;
  104. delete restProps.defaultOpen; // for rc-select not affected
  105. const inputClass = classNames(styles.input, {
  106. [styles.show]: searchMode,
  107. });
  108. return (
  109. <span
  110. className={classNames(className, styles.headerSearch)}
  111. onClick={this.enterSearchMode}
  112. onTransitionEnd={({ propertyName }) => {
  113. if (propertyName === 'width' && !searchMode) {
  114. const { onVisibleChange } = this.props;
  115. onVisibleChange(searchMode);
  116. }
  117. }}
  118. >
  119. <Icon type="search" key="Icon" />
  120. <AutoComplete
  121. key="AutoComplete"
  122. {...restProps}
  123. className={inputClass}
  124. value={value}
  125. onChange={this.onChange}
  126. >
  127. <Input
  128. ref={node => {
  129. this.inputRef = node;
  130. }}
  131. aria-label={placeholder}
  132. placeholder={placeholder}
  133. onKeyDown={this.onKeyDown}
  134. onBlur={this.leaveSearchMode}
  135. />
  136. </AutoComplete>
  137. </span>
  138. );
  139. }
  140. }