glass-app-manager
Version:
Informatica's Glass Framework CLI for bootstrapping
195 lines (174 loc) • 6.27 kB
JavaScript
// @flow
import * as React from "react";
import Downshift from "downshift/dist/downshift.cjs";
import { filterOptions } from "./utils";
type OptionDefinition = {
text: string,
value: any,
};
type Options = Array<OptionDefinition>;
type DownshiftWrapperProps = {
onChange?: () => void,
options: Options,
placeholder?: string,
search?: boolean,
selectedItems?: any,
setSelectedItem: OptionDefinition => void,
setUserInput: boolean => void,
setVisibleOptions: Options => void,
};
export default function DownshiftWrapper(props: DownshiftWrapperProps) {
const {
inputValue,
multiple,
options,
search,
selectedItems,
setInputValue,
setSelectedItem,
setUserInput,
setVisibleOptions,
} = props;
const addItem = React.useCallback((itemArray, item) => {
return [...itemArray, item];
}, []);
const removeItem = React.useCallback((itemArray, item) => {
return itemArray.filter(i => i !== item);
}, []);
const handleChange = React.useCallback(
item => {
if (multiple) {
let prevSelectedItems = selectedItems;
if (prevSelectedItems.includes(item)) {
prevSelectedItems = removeItem(prevSelectedItems, item);
} else {
prevSelectedItems = addItem(prevSelectedItems, item);
}
setSelectedItem(prevSelectedItems);
props.onChange(prevSelectedItems);
} else {
setSelectedItem(item);
props.onChange(item);
}
},
[multiple, options, selectedItems, setSelectedItem]
);
const handleInputValueChange = React.useCallback(
(changes, downshiftState) => {
switch (downshiftState.type) {
case Downshift.stateChangeTypes.keyDownEscape:
setUserInput(false);
setVisibleOptions(options);
if (multiple) {
setInputValue("");
} else {
setInputValue(changes);
}
break;
case Downshift.stateChangeTypes.changeInput:
if (search) {
setVisibleOptions(filterOptions(options, changes));
}
setUserInput(true);
setInputValue(changes);
break;
default:
}
},
[filterOptions, options, setUserInput, setVisibleOptions]
);
const handleOuterClick = React.useCallback(
downshiftState => {
if (multiple) {
setInputValue("");
} else {
setInputValue(downshiftState.selectedItem ? downshiftState.selectedItem.text : "");
}
setUserInput(false);
setVisibleOptions(options);
},
[options, setInputValue, setUserInput, setVisibleOptions]
);
const handleSelect = React.useCallback(
item => {
if (!multiple) {
setInputValue(item.text);
setUserInput(false);
setVisibleOptions(options);
}
},
[multiple, options, setInputValue, setUserInput, setVisibleOptions]
);
const handleStateChange = React.useCallback(
(changes, state) => {
let selectedItem = changes.selectedItem ? changes.selectedItem : state.selectedItem;
if (changes.hasOwnProperty("isOpen")) {
if (changes.isOpen) {
props.onOpen && props.onOpen(selectedItem);
} else {
props.onClose && props.onClose(selectedItem);
}
} else if (
multiple &&
!state.isOpen &&
changes.type === Downshift.stateChangeTypes.controlledPropUpdatedSelectedItem
) {
props.onClose && props.onClose(selectedItem);
}
},
[props.onOpen, props.onClose]
);
const stateReducer = React.useCallback(
(state, changes) => {
switch (changes.type) {
case Downshift.stateChangeTypes.changeInput:
if (search) {
return changes;
}
return {
...changes,
highlightedIndex: options.findIndex(option =>
option.text.toUpperCase().includes(changes.inputValue.toUpperCase())
),
};
case Downshift.stateChangeTypes.clickItem:
case Downshift.stateChangeTypes.keyDownEnter:
if (multiple) {
return {
...changes,
highlightedIndex: state.highlightedIndex,
isOpen: true,
};
}
return changes;
default:
return changes;
}
},
[multiple, options]
);
const getStateAndHelpers = React.useCallback(
downshift => {
return {
handleChange,
...downshift,
};
},
[handleChange]
);
const { render, children = render } = props;
return (
<Downshift
{...props}
inputValue={inputValue}
onChange={React.useCallback(e => handleChange(e), [handleChange])}
onInputValueChange={handleInputValueChange}
onOuterClick={handleOuterClick}
onSelect={handleSelect}
onStateChange={handleStateChange}
selectedItem={selectedItems}
stateReducer={stateReducer}>
{downshift => children(getStateAndHelpers(downshift))}
</Downshift>
);
}