UNPKG

ldx-widgets

Version:

widgets

310 lines (279 loc) 12 kB
(function() { var PropTypes, Pvr, React, SelectPvr, SelectPvrOption, assign, cloneDeep, createClass, div, filter, isEqual; React = require('react'); PropTypes = require('prop-types'); createClass = require('create-react-class'); assign = require('lodash/assign'); cloneDeep = require('lodash/cloneDeep'); filter = require('lodash/filter'); isEqual = require('lodash/isEqual'); Pvr = React.createFactory(require('./pvr')); SelectPvrOption = React.createFactory(require('./select_pvr_option')); div = require('react-dom-factories').div; /*& @general Select popover menu with sub-option capability @props.options - [Array] - Required array of objects containing at minimum a label and value attribute optionally a subLabel property can be passed @props.defaultSelected - [Object|String] - Optional value of the option selected by default @props.close - [Function] - Required func that closes the popover @props.styleMixin - [Object] - Optional object containing any style properties to mixin with and/or overrride the defaults note that width height are passed separately so they can have defaults and auto settings passing widt/height in this object could cause issues @props.onChange - [Function] - Required method to call when the non selected option is clicked @props.hideSelected - [Boolean] - Optional when on, the defaultSelected option will be removed from the list @props.headerTitle - [String] - Optional optional title String for popover header @props.headerClass - [String] - Optional optional class for popover header @props.maxHeight - [Number] - Optional the maximum height the popover should be. used to set height on the pvr if this is lower than the computed height. @props.pvrProps - [Object] - Optional properties germane to PVR wrapper: width, height, anchor, hAdjust, vAdjust, direction @props.multiSelect - [Boolean] - Optional allows the component to have multiple selections before firing the onChange handler. when multiSelect is enabled, onChange will be deferred to componentWillUnmount @props.multiSelectKey - [String|Number] - Optional `default: 'id'` a custom property name can be defined for keying multiSelect options. must be a unique identifier. @props.multiSelectGroupId - [String|Number] - Optional when `multiSelect = true`, selection scoping can be achieved by providing a group ID to separate selection groups. @props.footerComponent - [Component] - Optional Create a footer space at the bottom of the SelectPvr with a passed component @props.footerHeight - [Number] - Optional Set a fixed height to pass as calculated to the Pvr parent component. Used to size the footer space. & */ SelectPvr = createClass({ displayName: 'SelectPvr', propTypes: { options: PropTypes.array.isRequired, styleMixin: PropTypes.object, headerClass: PropTypes.string, close: PropTypes.func.isRequired, optionHeight: PropTypes.number, onChange: PropTypes.func.isRequired, maxHeight: PropTypes.number, pvrProps: PropTypes.object, canDeselect: PropTypes.bool, noWrapOptions: PropTypes.bool, defaultSelected: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), multiSelect: PropTypes.bool, multiSelectKey: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), multiSelectGroupId: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), footerComponent: PropTypes.object, footerHeight: PropTypes.number }, getInitialState: function() { var defaultSelected, optionHeight, options, ref; ref = this.props, defaultSelected = ref.defaultSelected, options = ref.options, optionHeight = ref.optionHeight; return { selectedOptions: defaultSelected, aggregateOptionsHeight: options.length * optionHeight + options.length * 1, openSubOptions: null }; }, getDefaultProps: function() { return { options: [], styleMixin: {}, headerTitle: null, headerClass: '', hideSelected: false, optionHeight: 36, noWrapOptions: false, pvrProps: {}, canDeselect: false, multiSelect: false, multiSelectKey: 'id', footerComponent: null, footerHeight: 0, defaultSelected: null }; }, componentWillMount: function() { var multiSelect, options, ref; ref = this.props, multiSelect = ref.multiSelect, options = ref.options; this.multiSelectGroupIds = new Map(); if (multiSelect) { return options.forEach((function(_this) { return function(o) { var multiSelectGroupId; multiSelectGroupId = o.multiSelectGroupId; if (_this.multiSelectGroupIds.has(multiSelectGroupId)) { return _this.multiSelectGroupIds.set(multiSelectGroupId, _this.multiSelectGroupIds.get(multiSelectGroupId) + 1); } else { return _this.multiSelectGroupIds.set(multiSelectGroupId, 1); } }; })(this)); } }, componentWillUnmount: function() { var onChange, selectedOptions; onChange = this.props.onChange; selectedOptions = this.state.selectedOptions; if (this.changeOnUnmount) { return onChange(selectedOptions); } }, render: function() { var aggregateOptionsHeight, canDeselect, childItems, children, chld, className, close, disabled, footerComponent, footerHeight, headerClass, headerTitle, hideSelected, i, id, j, label, len, len1, maxHeight, multiSelect, multiSelectGroupId, noWrapOptions, openSubOptions, opth, option, optionEls, optionHeight, options, optionsEqual, pvrProps, ref, ref1, ref2, scale, selectedOptions, style, styleMixin, subLabel, subOptionsHeight, value; ref = this.props, styleMixin = ref.styleMixin, options = ref.options, hideSelected = ref.hideSelected, optionHeight = ref.optionHeight, headerTitle = ref.headerTitle, headerClass = ref.headerClass, noWrapOptions = ref.noWrapOptions, disabled = ref.disabled, className = ref.className, maxHeight = ref.maxHeight, close = ref.close, canDeselect = ref.canDeselect, multiSelect = ref.multiSelect, footerComponent = ref.footerComponent, footerHeight = ref.footerHeight; ref1 = this.state, selectedOptions = ref1.selectedOptions, scale = ref1.scale, aggregateOptionsHeight = ref1.aggregateOptionsHeight, openSubOptions = ref1.openSubOptions; style = {}; pvrProps = cloneDeep(this.props.pvrProps); this.hasHeader = headerTitle != null; if (pvrProps.height == null) { pvrProps.height = aggregateOptionsHeight - (hideSelected ? optionHeight : 0); } if (this.hasHeader) { pvrProps.height += 34; } if (footerComponent != null) { pvrProps.height += footerHeight; } if ((maxHeight != null) && pvrProps.height > maxHeight) { pvrProps.height = maxHeight; } assign(style, styleMixin); if (((ref2 = pvrProps.styleMixin) != null ? ref2.maxHeight : void 0) != null) { style.maxHeight = pvrProps.styleMixin.maxHeight; } style.height = pvrProps.height; if (pvrProps.width) { style.width = pvrProps.width; } optionEls = []; for (i = 0, len = options.length; i < len; i++) { option = options[i]; children = option.children, label = option.label, id = option.id, value = option.value, subLabel = option.subLabel, multiSelectGroupId = option.multiSelectGroupId; optionsEqual = this.compareOptions(option); if (hideSelected && optionsEqual) { continue; } childItems = []; subOptionsHeight = 0; if (children != null) { for (j = 0, len1 = children.length; j < len1; j++) { chld = children[j]; if (!(chld != null)) { continue; } opth = chld.optionHeight || optionHeight; subOptionsHeight += opth; childItems.push(SelectPvrOption({ key: chld.id || chld.value, hasSubLabel: chld.subLabel != null, option: chld, optionHeight: opth, isSelected: this.compareOptions(chld), multiSelect: multiSelect, multiSelectGroupId: chld.multiSelectGroupId, canDeselect: chld.canDeselect, handleChange: chld.handleChange || this.handleChange, noWrapOptions: chld.noWrapOptions, customClass: chld.customClass })); } } optionEls.push(SelectPvrOption({ hasSubLabel: subLabel != null, option: option, optionHeight: optionHeight, key: id || value, isSelected: optionsEqual, canDeselect: canDeselect, handleChange: this.handleChange, noWrapOptions: noWrapOptions, subOptionsHeight: subOptionsHeight, setOpenSubOptions: this.setOpenSubOptions, multiSelect: multiSelect, multiSelectGroupId: multiSelectGroupId, isOpen: childItems.length && openSubOptions === option }, childItems)); } pvrProps.scale = scale; pvrProps.close = close; pvrProps.element = div({ key: 'select-pvr', className: 'select-pvr', style: style }, [ this.hasHeader ? div({ key: 'header', className: "header plain-pvr-content-item " + headerClass }, headerTitle) : void 0, div({ key: 'inner-rows' }, optionEls), footerComponent != null ? footerComponent : void 0 ]); return Pvr(pvrProps); }, handleChange: function(option, multiSelectGroupId) { var multiSelect, multiSelectKey, opt, ref, ref1, selectedOptions; ref = this.props, multiSelect = ref.multiSelect, multiSelectKey = ref.multiSelectKey; selectedOptions = this.state.selectedOptions; this.changeOnUnmount = this.multiSelectGroupIds.get(multiSelectGroupId) > 1; opt = {}; if (multiSelect) { assign(opt, selectedOptions); if ((multiSelectGroupId != null) && multiSelectGroupId !== ((ref1 = selectedOptions[Object.keys(selectedOptions)[0]]) != null ? ref1.multiSelectGroupId : void 0)) { opt = {}; } if (opt[option[multiSelectKey]] != null) { delete opt[option[multiSelectKey]]; } else { opt[option[multiSelectKey]] = option; } } else { opt = option; } this.setState({ selectedOptions: opt }); if (!this.changeOnUnmount) { this.props.onChange(opt); return this.props.close(); } }, setOpenSubOptions: function(option, adjust) { var agg, optionHeight, options, ref; ref = this.props, options = ref.options, optionHeight = ref.optionHeight; agg = options.length * optionHeight + (options.length * 1) + adjust; return this.setState({ openSubOptions: option, aggregateOptionsHeight: agg }); }, compareOptions: function(option) { var k, label, multiSelect, o, selectedOptions; selectedOptions = this.state.selectedOptions; multiSelect = this.props.multiSelect; label = option.label; if (typeof selectedOptions === 'string') { return selectedOptions === label; } else if (typeof selectedOptions === 'object') { if (multiSelect) { for (k in selectedOptions) { o = selectedOptions[k]; if (isEqual(o, option)) { return true; } } return false; } else { return isEqual(selectedOptions, option); } } } }); module.exports = SelectPvr; }).call(this);