text: import React, { PureComponent } from "react";
  import {
    getSelectedByAllItems,
    filterUnselectedByIds,
    findItem,
    getMinMaxIndexes,
    isWithin,
    getNewSelectedItems
  } from "./multi_select_state_utils";
  
  const withMultiSelectState = WrappedComponent =>
    class extends PureComponent {
      static defaultProps = {
        filterFunction: value => item =>
          String(item.label)
            .toLowerCase()
            .includes(value.toLowerCase()),
        items: [],
        selectedItems: [],
        isLocked: item => item.disabled
      };
  
      constructor(props) {
        super(props);
        this.selectItem = this.selectItem.bind(this);
        this.unselectItems = this.unselectItems.bind(this);
        this.filterItems = this.filterItems.bind(this);
        this.selectAllItems = this.selectAllItems.bind(this);
        this.isAllSelected = this.isAllSelected.bind(this);
        this.clearAll = this.clearAll.bind(this);
        this.handleChange = this.handleChange.bind(this);
        this.getList = this.getList.bind(this);
        this.onKeyUp = this.onKeyUp.bind(this);
        this.filterSelectedItems = this.filterSelectedItems.bind(this);
        this.getNewFilteredSelectedItems = this.getNewFilteredSelectedItems.bind(
          this
        );
        const { items, selectedItems } = props;
        this.selectedItemsFilterFunction =
          props.selectedItemsFilterFunction || props.filterFunction;
        this.state = {
          selectedItems,
          items,
          filteredItems: items,
          filteredSelectedItems: selectedItems
        };
      }
  
      componentWillReceiveProps(nextProps) {
        const { searchValue, searchSelectedItemsValue } = this.props;
        if (this.props.selectedItems !== nextProps.selectedItems) {
          this.setState({
            selectedItems: nextProps.selectedItems,
            filteredSelectedItems: nextProps.selectedItems
          });
        }
        if (this.props.items !== nextProps.items) {
          const { items } = nextProps;
          this.setState({ items, filteredItems: items });
        }
        if (searchValue !== nextProps.searchValue) {
          this.filterItems({ target: { value: nextProps.searchValue } });
        }
        if (searchSelectedItemsValue !== nextProps.searchSelectedItemsValue) {
          this.filterSelectedItems({
            target: { value: nextProps.searchSelectedItemsValue }
          });
        }
      }
  
      handleMultiSelection(index) {
        const { items, isLocked } = this.props;
        const { filteredItems, firstItemShiftSelected } = this.state;
  
        const interval = getMinMaxIndexes(index, firstItemShiftSelected);
        const newSelectedItems = items.filter(
          (item, index) =>
            (isWithin(index, interval) &&
              !isLocked(item) &&
              findItem(item, filteredItems)) ||
            findItem(item, this.state.selectedItems)
        );
        const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
          newSelectedItems
        );
        this.setState(
          {
            selectedItems: newSelectedItems,
            filteredSelectedItems: newFilteredSelectedItems
          },
          this.handleChange
        );
      }
  
      getNewFilteredSelectedItems(items) {
        const { searchSelectedItemsValue } = this.props;
  
        return searchSelectedItemsValue
          ? items.filter(
              this.selectedItemsFilterFunction(searchSelectedItemsValue)
            )
          : items;
      }
      componentDidMount() {
        window.addEventListener("keyup", this.onKeyUp);
      }
      componentWillUnmount() {
        window.removeEventListener("keyup", this.onKeyUp, false);
      }
  
      onKeyUp(event) {
        if (event.keyCode === 16) {
          this.setState({ firstItemShiftSelected: undefined });
        }
      }
  
      selectItem(event, id) {
        const { items, keepSelectionOrder } = this.props;
        const { selectedItems, firstItemShiftSelected } = this.state;
        if (!selectedItems.find(item => item.id === id)) {
          if (event.shiftKey && firstItemShiftSelected !== undefined) {
            this.handleMultiSelection(items.findIndex(item => item.id === id));
          } else {
            if (event.shiftKey && firstItemShiftSelected === undefined) {
              const index = items.findIndex(item => item.id === id);
              this.setState({ firstItemShiftSelected: index });
            }
            this.setNewItemsBySelectItem(
              id,
              items,
              selectedItems,
              keepSelectionOrder
            );
          }
        } else {
          this.unselectItems([id]);
        }
      }
  
      setNewItemsBySelectItem(id, items, selectedItems, keepSelectionOrder) {
        const newSelectedItems = getNewSelectedItems(
          id,
          items,
          selectedItems,
          keepSelectionOrder
        );
        const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
          newSelectedItems
        );
        this.setState(
          {
            selectedItems: newSelectedItems,
            filteredSelectedItems: newFilteredSelectedItems
          },
          this.handleChange
        );
      }
      unselectItems(ids) {
        const { selectedItems, filteredSelectedItems } = this.state;
        const { isLocked } = this.props;
        const newSelectedItems = filterUnselectedByIds(
          selectedItems,
          ids,
          isLocked
        );
        const newFilteredSelectedItems = filterUnselectedByIds(
          filteredSelectedItems,
          ids,
          isLocked
        );
        this.setState(
          {
            selectedItems: newSelectedItems,
            filteredSelectedItems: newFilteredSelectedItems
          },
          this.handleChange
        );
      }
  
      clearAll() {
        const { selectedItems, isLocked } = this.props;
        const lockedItems = selectedItems.filter(isLocked);
        this.setState(
          {
            selectedItems: lockedItems,
            filteredSelectedItems: lockedItems
          },
          this.handleChange
        );
      }
  
      filterItems(event) {
        const { items, filterFunction, searchValueChanged } = this.props;
        const { value } = event.target;
        this.setState({ filteredItems: items.filter(filterFunction(value)) });
        searchValueChanged && searchValueChanged(value);
      }
  
      filterSelectedItems(event) {
        const { searchSelectedItemsChanged } = this.props;
        const { value } = event.target;
        const { selectedItems } = this.state;
        this.setState({
          filteredSelectedItems: selectedItems.filter(
            this.selectedItemsFilterFunction(value)
          )
        });
        searchSelectedItemsChanged && searchSelectedItemsChanged(value);
      }
  
      selectAllItems() {
        const { filteredItems, selectedItems } = this.state;
        const { items } = this.props;
        if (this.isAllSelected()) {
          this.unselectItems(filteredItems.map(filteredItem => filteredItem.id));
        } else {
          const newSelectItems = getSelectedByAllItems(
            filteredItems,
            selectedItems,
            items
          );
          const newFilteredSelectedItems = this.getNewFilteredSelectedItems(
            newSelectItems
          );
          this.setState(
            {
              selectedItems: newSelectItems,
              filteredSelectedItems: newFilteredSelectedItems
            },
            this.handleChange
          );
        }
      }
  
      isAllSelected() {
        const { filteredItems, selectedItems } = this.state;
        const { isLocked } = this.props;
        const selectedItemsInFilteredItems = selectedItems.filter(
          selectedItem =>
            !isLocked(selectedItem) && findItem(selectedItem, filteredItems)
        );
        const selectableFilteredItems = filteredItems.filter(
          item => !isLocked(item)
        );
        return (
          selectedItemsInFilteredItems.length ===
            selectableFilteredItems.length && selectableFilteredItems.length > 0
        );
      }
  
      handleChange() {
        const { onChange } = this.props;
        const { selectedItems } = this.state;
        this.list && this.list.update();
        onChange && onChange(selectedItems);
      }
  
      getList(ref) {
        this.list = ref;
      }
  
      render() {
        return (
          <WrappedComponent
            {...this.props}
            {...this.state}
            unselectItems={this.unselectItems}
            selectItem={this.selectItem}
            filterItems={this.filterItems}
            selectAllItems={this.selectAllItems}
            isAllSelected={this.isAllSelected()}
            clearAll={this.clearAll}
            getList={this.getList}
            filterSelectedItems={this.filterSelectedItems}
          />
        );
      }
    };
  
  export default withMultiSelectState;
  