jobiqo-cl
Version:
[](https://circleci.com/gh/jobiqo/jobiqo-cl)
225 lines (219 loc) • 9.61 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var React = require('react');
var React__default = _interopDefault(React);
var styled = require('styled-components');
var styled__default = _interopDefault(styled);
var index = require('../../11-form-item/index.js');
var downshift_esm = require('../../../../../../node_modules/downshift/dist/downshift.esm.js');
var matchSorter_esm = require('../../../../../../node_modules/match-sorter/dist/match-sorter.esm.js');
var subElements = require('./sub-elements/sub-elements.js');
/**
* @file index.tsx
*
* @fileoverview Renders a multi element select component using the downshift library.
*/
const ItemLabel = styled__default.span `
display: inlineBlock;
padding: ${props => props.theme.select.dropMenu.itemSelected.padding};
color: ${props => props.theme.select.dropMenu.itemSelected.color};
background: ${props => props.theme.select.dropMenu.itemSelected.background};
text-transform: uppercase;
border-radius: ${props => props.theme.select.dropMenu.itemSelected.borderRadius};
margin-left: auto;
font-size: ${props => props.theme.select.dropMenu.itemSelected.fontSize};
margin-left: 0.5rem;
`;
const InputWrapper = styled__default.div `
outline: 0;
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
transition: box-shadow 0.1s ease, width 0.1s ease;
font-size: ${props => props.theme.select.fontSize};
margin: ${props => props.theme.select.margin};
padding: ${props => props.theme.select.multiple.padding};
border-radius: ${props => props.theme.select.borderRadius};
border-width: ${props => props.theme.select.borderWidth};
border-style: ${props => props.theme.select.borderStyle};
border-color: ${props => props.error ? props.theme.colors.danger : props.theme.select.borderColor};
background: ${props => props.theme.select.background};
max-width: ${props => props.theme.select.multiple.input.width};
&:focus-within {
outline: 0;
border-color: ${props => props.theme.colors.focusRingColor};
box-shadow: ${props => props.theme.select.focus.boxShadowType},
${props => `${props.theme.select.focus.boxShadowVals} ${props.theme.colors.focusRingColor}`};
}
`;
const DownshiftInput = styled__default.input `
word-wrap: break-word;
outline: 0;
background: transparent;
display: inline-block;
box-shadow: none;
border: 0;
margin: ${props => props.theme.select.multiple.input.margin};
font-size: ${props => props.theme.select.fontSize};
width: 50%;
`;
const SpanElement = styled__default.span `
margin: ${props => props.theme.select.multiple.tags.margin};
font-size: ${props => props.theme.select.multiple.tags.fontSize};
padding: ${props => props.theme.tag.padding};
border-radius: ${props => props.theme.tag.borderRadius};
background: ${props => props.theme.tag.background};
border-width: ${props => props.theme.tag.borderWidth};
border-style: ${props => props.theme.tag.borderStyle};
border-color: ${props => props.theme.tag.borderColor};
`;
const ItemsWrapper = styled__default.div `
background: ${props => props.theme.select.dropMenu.background};
max-height: ${props => props.theme.select.dropMenu.maxHeight};
border-right-width: ${props => props.theme.select.dropMenu.borderWidth};
border-bottom-width: ${props => props.theme.select.dropMenu.borderWidth};
border-left-width: ${props => props.theme.select.dropMenu.borderWidth};
border-radius: ${props => props.theme.select.dropMenu.borderRadius};
border-color: ${props => props.theme.select.dropMenu.borderColor};
box-shadow: ${props => props.theme.select.dropMenu.boxShadow};
border-top-width: 0;
overflow-y: auto;
overflow-x: hidden;
outline: 0;
transition: opacity 0.1s ease;
border-style: solid;
`;
const parentStyle = {
width: '100%'
};
/**
* A multiple autotocomplet widget for when the user needs to select multiple elements in a select-style element.
*/
class MultipleAutocomplete extends React__default.Component {
constructor() {
super(...arguments);
this.state = {
input: '',
selectedItems: []
};
/**
* handles the key down event which will show all items in the dropdown.
*
* @param {any} evt
* The event of key down.
*/
this.handleKeyDown = (evt) => {
const { values } = this.props;
if (values.length && !this.state.input.length && evt.keyCode === 8) {
this.props.onChange(values.slice(0, values.length - 1));
}
};
/**
* Handles an input change when we type something in the input box.
*
* Will set the internal state with what was typed.
*
* @param {any} evt
* The event.
*/
this.handleInputChange = (evt) => {
this.setState({ input: evt.target.value });
if (this.props.onInputChange) {
this.props.onInputChange(evt.target.value);
}
};
/**
* Handles a change event on the dropdwon element.
*
* When we actually choose something in the dropdown this will set the onChange event (fire external)
* and will also clear the input.
*
* @param {SelectItem} item
* The item that was just selected.
*/
this.handleChange = (item) => {
this.props.onChange([...this.props.values, item.code]);
this.setState({ input: '' });
};
}
/**
* Based on the key of a item return its full item with name as well.
*
* @param {string} key
* The key to get the item from.
*/
getItemNameFormSelectedItem(key) {
const items = this.props.items.filter(item => item.code === key);
if (items.length > 0) {
return items[0].name;
}
return null;
}
render() {
const { id, values, placeholder, label, required = false, error = false, onBlur } = this.props;
// We get a new input from the user. We need to check for the names that match. And then we
// map to selectedItem types.
const { input } = this.state;
const itemNames = input.length
? matchSorter_esm.default(this.props.items.map(i => i.name), input)
: this.props.items.map(i => i.name);
const items = this.props.items.filter(item => {
return itemNames.includes(item.name);
});
const indices = mapItemIndex(items, values);
return (React__default.createElement(index.FormItem, null,
label && (React__default.createElement(index.FormLabel, { error: error, required: required, htmlFor: id }, label)),
React__default.createElement(downshift_esm.default, { inputValue: this.state.input, onChange: this.handleChange, selectedItem: values, itemToString: item => (item ? item.code : '') }, ({ getInputProps, getItemProps, getRootProps, getLabelProps, highlightedIndex, isOpen, selectedItem }) => (React__default.createElement("div", { style: parentStyle },
React__default.createElement(InputWrapper, { error: error },
selectedItem.map((value, i) => (React__default.createElement(SpanElement, { key: value }, this.getItemNameFormSelectedItem(value)))),
React__default.createElement(DownshiftInput, Object.assign({}, getInputProps({
placeholder,
id,
onChange: this.handleInputChange,
onKeyDown: this.handleKeyDown,
onBlur
})))),
isOpen && (React__default.createElement(ItemsWrapper, null, items.map(item => {
const selected = this.props.values.indexOf(item.code) !== -1;
const props = selected
? {}
: getItemProps({
item,
index: indices[item.code],
isSelected: selected
});
return (React__default.createElement(subElements.Item, Object.assign({}, props, { isActive: highlightedIndex === indices[item.code], key: item.code }),
item.name,
selected && React__default.createElement(ItemLabel, null, "Selected")));
}))))))));
}
}
MultipleAutocomplete.defaultProps = {
placeholder: 'Search something here...',
items: []
};
/**
* Get the real index of an item.
*
* It does this filtering out selected values, and mapping the values to the index.
* We're doing so that Downshift doesn't recognize selected item,
* thus won't highlight the selected item.
*
* Given that ['Black', 'Blue', 'Green'], and 'Blue' is selected
* Output: { 'Black': 0, 'Green': 1 }
*
* @param {SelectItem[]} items items
* @param {String[]} values selected items
* @return {Object} Mapping of selected items to their indices
*/
function mapItemIndex(items, values) {
return items
.filter(item => values.indexOf(item.code) === -1)
.reduce((prev, next, i) => {
prev[next.code] = i;
return prev;
}, {});
}
exports.MultipleAutocomplete = MultipleAutocomplete;