ldx-widgets
Version:
widgets
330 lines (304 loc) • 11.3 kB
JavaScript
(function() {
var FormValidation, MultiSelect, MultiSelectOption, React, SearchInput, _, button, div, li, ref, ul;
React = require('react');
_ = require('lodash');
MultiSelectOption = React.createFactory(require('./multi_select_option'));
SearchInput = React.createFactory(require('./search_input'));
FormValidation = require('../mixins/form_validation');
ref = React.DOM, div = ref.div, button = ref.button, ul = ref.ul, li = ref.li;
/*
Multi Select Props
@props.options - REQUIRED - Array
An array of options for the user to click - can be a flat array (where each entry is both the value and label)
or an array of objects, where the value is the `valueField` and the label is the `labelField` (see below)
@props.values - OPTIONAL - Array
A flat array of values that the control initializes to. If the optons arrya is objects, each entry should be the
`valueField` value. Will default to an empty array
@props.labelField - OPTIONAL - String
The attribute name from each option object that is the 'value`
@props.valueField - OPTIONAL - String
The attribute name from each option object that is the user faciing label
@props.onChange - OPTIONAL - function
Function/method to fire when the data changes
@props.filter - OPTIONAL - Boolean - default: 'auto'
Show text filter? auto will show it whenever the options list is longer than 4
@props.allowDefault - OPTIONAL - Boolean - default: false
Allow one entry to be set as the default
@props.valueOfDefault - OPTIONAL - used in conjunction w/ allowDefault
The value of the option that is currently set as the default
@props.searchPlaceholder - OPTIONAL
placeholder text for the add button
@props.editPlaceholder - OPTIONAL
placeholder text for the filter field
@props.tabIndex - OPTIONAL
tab order of the edi button
@props.returnFullObjects - OPTIONAL - Boolean - default: false
whether or not the getFormData method should return a collection of selected objects or a flat array
@props.onRemove - OPTIONAL - function
function call when an item is removed, will pass the removed item
*/
MultiSelect = React.createClass({
displayName: 'MultiSelect',
mixins: [FormValidation],
getDefaultProps: function() {
return {
filter: 'auto',
allowDefault: false,
editPlaceholder: 'Edit Items',
searchPlaceholder: 'Filter Items',
tabIndex: -1,
returnFullObjects: false,
onRemove: function() {}
};
},
render: function() {
var allowDefault, buttonText, disabled, editPlaceholder, filter, filterTerm, i, isActive, j, len, len1, notSelected, option, otherOptions, ref1, ref2, searchPlaceholder, selected, selectedOptions, tabIndex, theDefault;
ref1 = this.state, selected = ref1.selected, notSelected = ref1.notSelected, isActive = ref1.isActive, theDefault = ref1.theDefault, filterTerm = ref1.filterTerm;
ref2 = this.props, editPlaceholder = ref2.editPlaceholder, searchPlaceholder = ref2.searchPlaceholder, allowDefault = ref2.allowDefault, filter = ref2.filter, tabIndex = ref2.tabIndex, disabled = ref2.disabled;
selectedOptions = [];
otherOptions = [];
this.getErrors();
for (i = 0, len = selected.length; i < len; i++) {
option = selected[i];
selectedOptions.push(MultiSelectOption({
key: option.value,
option: option,
allowDefault: allowDefault,
isActive: isActive,
setValues: this.setValues,
disabled: disabled,
setDefault: this.setDefault,
tabIndex: tabIndex,
isTheDefault: option === theDefault,
onRemove: this.props.onRemove
}));
}
if (selectedOptions.length === 0) {
selectedOptions.push(li({
key: 'none',
className: 'multiselect-none'
}, ['None']));
}
if (isActive) {
for (j = 0, len1 = notSelected.length; j < len1; j++) {
option = notSelected[j];
if (option.isVisible) {
otherOptions.push(MultiSelectOption({
key: option.value,
option: option,
allowDefault: allowDefault,
isActive: isActive,
tabIndex: tabIndex,
setValues: this.setValues
}));
}
}
}
buttonText = "+ " + editPlaceholder;
return div({
className: 'multiselect field-wrap' + this.invalidClass,
onClick: this.toggleOn
}, [
ul({
key: 'selectedList',
ref: 'selectedList',
className: 'multiselect-list-in'
}, selectedOptions), this.filterShouldBeShown() ? SearchInput({
ref: 'filterField',
key: 'filter-input',
placeholder: searchPlaceholder,
handleChange: this.handlefilter,
wrapClass: 'multi-select-filter',
width: '100%',
focusOnMount: true,
disabled: disabled
}) : void 0, !(isActive || disabled) ? button({
key: 'addButton',
className: 'multiselect-edit',
tabIndex: tabIndex,
onFocus: this.toggleOn
}, buttonText) : void 0, isActive ? ul({
key: 'notSelectedList',
ref: 'notSelectedList',
className: 'multiselect-list-out'
}, otherOptions) : void 0, this.validationErrors.length ? div({
className: 'field-errors-show',
key: 'textInputErrorsShow'
}, [
div({
className: 'field-errors',
key: 'textInputErrors'
}, ul({
className: 'field-errors-list'
}, this.validationErrors))
]) : void 0
]);
},
componentDidMount: function() {
return document.addEventListener('click', this.blur);
},
componentWillUnmount: function() {
return document.removeEventListener('click', this.blur);
},
getInitialState: function() {
var allowDefault, i, labelField, len, newOption, option, options, ref1, selected, theDefault, valueField, valueOfDefault, values;
ref1 = this.props, options = ref1.options, values = ref1.values, labelField = ref1.labelField, valueField = ref1.valueField, valueOfDefault = ref1.valueOfDefault, allowDefault = ref1.allowDefault;
this.allOptions = [];
for (i = 0, len = options.length; i < len; i++) {
option = options[i];
newOption = {
isSelected: false,
isVisible: true
};
if (typeof option === 'object') {
if (!(((valueField != null) && (labelField != null)) || ((option.value != null) && (options.label != null)))) {
return typeof console !== "undefined" && console !== null ? console.error('MultiSelect requires labelField and valueField props when the options array is made up of objects') : void 0;
}
newOption.value = option[valueField];
newOption.label = option[labelField];
} else {
newOption.label = option.toString();
newOption.value = option;
}
if ((values != null) && values.indexOf(newOption.value) !== -1) {
newOption.isSelected = true;
}
this.allOptions.push(newOption);
theDefault = _.findWhere(this.allOptions, {
value: valueOfDefault
});
selected = _.where(this.allOptions, {
isSelected: true
});
if (theDefault == null) {
if (selected.length != null) {
theDefault = selected[0];
}
}
}
return {
selected: selected || [],
notSelected: _.where(this.allOptions, {
isSelected: false
}) || [],
isActive: false,
theDefault: theDefault,
filterTerm: ''
};
},
getFormData: function() {
var i, len, option, ref1, results, selectedValues;
selectedValues = _.pluck(_.where(this.allOptions, {
isSelected: true
}), 'value');
if (!this.props.returnFullObjects) {
return selectedValues;
}
ref1 = this.props.options;
results = [];
for (i = 0, len = ref1.length; i < len; i++) {
option = ref1[i];
if (selectedValues.indexOf(option[this.props.valueField]) !== -1) {
results.push(option);
}
}
return results;
},
toggleOn: function(e) {
var base;
if (this.props.disabled) {
return;
}
if (typeof (base = e.nativeEvent).stopImmediatePropagation === "function") {
base.stopImmediatePropagation();
}
if (!this.state.isActive) {
return this.setState({
isActive: true
}, (function(_this) {
return function() {
return _this.focusFirstOption();
};
})(this));
}
},
focusFirstOption: function() {
var firstSelected, firstUnselected;
firstUnselected = this.refs.notSelectedList.getElementsByTagName('button')[0];
firstSelected = this.refs.selectedList.getElementsByTagName('button')[0];
if (firstUnselected != null) {
return firstUnselected.focus();
} else {
return firstSelected.focus();
}
},
blur: function() {
return this.setState({
isActive: false
});
},
setDefault: function(newDefault) {
return this.setState({
theDefault: newDefault
});
},
setValues: function() {
var selected, theDefault;
theDefault = this.state.theDefault;
selected = _.where(this.allOptions, {
isSelected: true
});
if (selected.indexOf(theDefault) === -1) {
if (selected.length) {
theDefault = selected[0];
}
}
return this.setState({
selected: _.where(this.allOptions, {
isSelected: true
}),
notSelected: _.where(this.allOptions, {
isSelected: false
}),
theDefault: theDefault
}, function() {
var base;
if (this.filterShouldBeShown()) {
this.refs.filterField.focus();
} else {
this.focusFirstOption();
}
return typeof (base = this.props).onChange === "function" ? base.onChange() : void 0;
});
},
handlefilter: function(term) {
var i, item, j, len, len1, notSelected, ref1;
notSelected = _.where(this.allOptions, {
isSelected: false
});
if (term === '') {
ref1 = this.allOptions;
for (i = 0, len = ref1.length; i < len; i++) {
item = ref1[i];
item.isVisible = true;
}
} else {
for (j = 0, len1 = notSelected.length; j < len1; j++) {
item = notSelected[j];
if (item.label.toLowerCase().search(term.toLowerCase()) !== -1) {
item.isVisible = true;
} else {
item.isVisible = false;
}
}
}
return this.setState({
notSelected: notSelected
});
},
filterShouldBeShown: function() {
return this.state.isActive && (this.props.filter === true || (this.props.filter === 'auto' && this.allOptions.length > 4));
}
});
module.exports = MultiSelect;
}).call(this);