UNPKG

ldx-widgets

Version:

widgets

330 lines (304 loc) 11.3 kB
(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);