使用键盘向下箭头时,React Auto Complete组件关闭

时间:2019-01-17 10:21:41

标签: reactjs

我们正在构建一个自定义的React自动完成组件。

问题:当用户在文本字段中键入内容时,将显示匹配的建议,如果用户使用键盘的向下箭头进行选择,则一旦选择了向下箭头,下拉菜单就会关闭。

下面是代码,我无法提供此问题的实时示例

import React, { Component } from "react";
import {
  string,
  node,
  bool,
  element,
  func,
  arrayOf,
  shape,
  number
} from "prop-types";
import { MenuAnchor } from "@abc/react-menu";
import { TextField, TextFieldIcon } from "@abc/react-text-field";
import { DropDownMenu } from "./DropDownMenu";

export class Autocomplete extends Component {


  static defaultProps = {
    items: [],
    value: "",
    minChars: 3,
    noOptionsText: <span>{"No options"}</span>,
    textFieldClassName: "",
    focusTextFieldAfterSelection: false
  };

  state = {
    open: false,
    value: this.props.value || "",
    selected: this.props.value ? this.props.value : null,
    filtered: [],
    valueMap: this.props.items.reduce(reduceToMap, {})
  };

  focus = () => this.inputRef && this.inputRef.focus();

  isSameObjectShallowComparison = (obj1, obj2) =>
    Object.keys(obj1).length === Object.keys(obj2).length &&
    Object.keys(obj1).every(key => obj1[key] === obj2[key]);

  componentDidUpdate(prevProps, prevState) {
    const newValueMap = this.props.items.reduce(reduceToMap, {});
    // If the items are updated then update the valueMap in the state
    if (!this.isSameObjectShallowComparison(newValueMap, prevState.valueMap)) {
      this.setState({
        valueMap: newValueMap
      });
      const newSelectedValue = newValueMap[this.state.selected];
      // Update the value with the new selected value in the value map.
      if (this.state.value !== newSelectedValue && newSelectedValue)
        this.setState({
          value: newSelectedValue,
          open: false
        });
    }
  }

  onSelected = selected => {
    this.setState({
      selected,
      open: false,
      value: this.state.valueMap[selected]
    });
    this.props.onSelected && this.props.onSelected(selected);
    this.props.focusTextFieldAfterSelection && this.focus();
  };

  componentDidMount() {
    if (this.inputRef) {
      this.inputRef.addEventListener("keydown", this.onKeyPress);
      this.inputRef.addEventListener("focus", this.onFocus);
      this.inputRef.addEventListener("blur", this.onBlur);
    }

    this.filterItems(this.props.value);
  }

  componentWillUnmount() {
    if (this.inputRef) {
      this.inputRef.removeEventListener("keydown", this.onKeyPress);
      this.inputRef.removeEventListener("focus", this.onFocus);
      this.inputRef.removeEventListener("blur", this.onBlur);
    }
    clearTimeout(this.timeOut);
  }

  onKeyPress = e => {
    if (e.keyCode === 40 && this.menuRef && this.menuRef.childNodes) {
      e.preventDefault();
      e.stopPropagation();
      const firstChild = this.menuRef.childNodes[0];
      if (firstChild) {
        firstChild.focus();
      }
    }
  };

  onFocus = e => this.setState({ open: this.state.filtered.length > 0 });

  onBlur = e => {
    this.timeOut = setTimeout(() => {
      this.setState({ open: false });
      clearTimeout(this.timeOut);
    }, 175);
  };

  onChange = e => {
    const value = e.target.value;
    this.filterItems(value);

    this.setState({ open: value.length >= this.props.minChars });
    let newSelectedValue = this.state.selected;
    if (value.indexOf(newSelectedValue) === -1) {
      this.setState({ selected: null });
      newSelectedValue = null;
    }
    this.props.onChange && this.props.onChange(e, value, newSelectedValue);
  };

  filterItems(value) {
    const filtered = [];
    if (value && value.length >= this.props.minChars) {
      const { items } = this.props;
      const re = new RegExp(value, "i");
      for (let i = 0; i < items.length; i++) {
        if (items[i].label.search(re) > -1) {
          filtered.push({ ...items[i] });
        }
      }
      this.setState({ filtered, value });
    } else {
      this.setState({ value, filtered, open: false });
    }
  }

  clearSelection = () => {
    this.setState({ open: false, selected: null, value: "", filtered: [] });
    if (this.props.onSelected) {
      this.props.onSelected(null);
    }
  };

  render() {
    const {
      type,
      pattern,
      noOptionsText,
      leadingIcon,
      trailingIcon,
      inputRef,
      onChange,
      minChars,
      items,
      onSelected,
      value,
      textFieldClassName,
      focusTextFieldAfterSelection,
      ...other
    } = this.props;
    const { open, filtered } = this.state;
    return (
      <MenuAnchor>
        <TextField
          value={this.state.value}
          onChange={this.onChange}
          inputRef={ref => {
            this.inputRef = ref;
          }}
          className={`autocomplete--input ${textFieldClassName}`}
          trailingIcon={
            this.state.value.length > 0 ? (
              <TextFieldIcon
                onClick={this.clearSelection}
                className="-autocomplete--icon"
              >
                cancel
              </TextFieldIcon>
            ) : (
              <TextFieldIcon className="-autocomplete--icon" />
            )
          }
          {...other}
        />
        <DropDownMenu
          innerRef={ref => (this.menuRef = ref)}
          noOptionsText={noOptionsText}
          onSelected={this.onSelected}
          items={filtered}
          open={open}
          value={this.state.value}
        />
      </MenuAnchor>
    );
  }
}

function reduceToMap(prev, curr) {
  prev[curr.value] = curr.label;
  return prev;
}

如果您可以从上面的代码中找出一些内容,以防止单击帮助面板上的向下箭头时下拉菜单关闭。

0 个答案:

没有答案