ldx-widgets
Version:
widgets
310 lines (278 loc) • 10.9 kB
JavaScript
(function() {
var CircleXButton, DOWN_ARROW, ENTER, ESCAPE, InputMixin, LEFT_ARROW, OPTION_PADDING, PropTypes, Pvr, RIGHT_ARROW, React, SPACE, SelectInputCustom, SelectInputCustomOptions, TAB, UP_ARROW, alphaNumericKeyCode, createClass, div, find, option, ref, ref1, select;
React = require('react');
createClass = require('create-react-class');
PropTypes = require('prop-types');
find = require('lodash/find');
ref = require('../constants/keyboard'), DOWN_ARROW = ref.DOWN_ARROW, RIGHT_ARROW = ref.RIGHT_ARROW, LEFT_ARROW = ref.LEFT_ARROW, UP_ARROW = ref.UP_ARROW, ENTER = ref.ENTER, SPACE = ref.SPACE, ESCAPE = ref.ESCAPE, TAB = ref.TAB;
alphaNumericKeyCode = require('../utils').alphaNumericKeyCode;
InputMixin = require('../mixins/input_mixin');
CircleXButton = React.createFactory(require('./circle_x_button'));
SelectInputCustomOptions = React.createFactory(require('./select_input_custom_options'));
Pvr = React.createFactory(require('./pvr'));
ref1 = require('react-dom-factories'), div = ref1.div, select = ref1.select, option = ref1.option;
OPTION_PADDING = 10;
/*&
@general
Filterable select menu. This component lives on the overlay layer, and requires integrated context methods closeOverlay and openOverlay within the application.
@props.options - [Array] - Required
Full list of options to display on component
@props.value - [String|Object] - Optional
The value that corresponds to the option object with the matching value
@props.selectText - [String] - Optional
Text displayed as the default value
@props.onChange - [Function] - Required
Function fired when a change is made to the selection
@props.tabIndex - [Number] - Optional
Tab order index
@props.disabled - [Boolean] - Optional
Disabled state of the component
@props.isFilter - [Boolean] - Optional
Show/hide the filter typeahead input. Default is no
@props.returnFullObjects - [Boolean] - Optional
Determine whether `getValue` returns the full option object, or just the default value string
@props.placeholder - [String] - Optional
Placeholder text for the filter input
@props.valueField - [String] - Optional
The name of the key used to reference the value on the option object
@props.labelField - [String] - Optional
The name of the key used to reference the label on the option object
@props.width - [Number] - Optional
The width of the menu popover
@props.height - [Number] - Optional
The height of the menu popover
@props.optionHeight - [Number] - Optional
The fixed height of each menu option
&
*/
SelectInputCustom = createClass({
displayName: 'SelectInputCustom',
mixins: [InputMixin],
contextTypes: {
openOverlay: PropTypes.func,
closeOverlay: PropTypes.func,
clearValidationError: PropTypes.func,
addValidationError: PropTypes.func,
getValidationStatus: PropTypes.func,
toggleValidationError: PropTypes.func
},
propTypes: {
options: PropTypes.array.isRequired,
selectText: PropTypes.string,
onChange: PropTypes.func.isRequired,
tabIndex: PropTypes.number,
disabled: PropTypes.bool,
isFilter: PropTypes.bool,
returnFullObjects: PropTypes.bool,
placeholder: PropTypes.string,
valueField: PropTypes.string,
labelField: PropTypes.string,
width: PropTypes.number,
height: PropTypes.number,
optionHeight: PropTypes.number,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
nibColor: PropTypes.string
},
getDefaultProps: function() {
return {
options: [],
selectText: 'Select from list...',
placeholder: 'Filter options',
valueField: 'value',
labelField: 'title',
width: 250,
height: 400,
isFilter: false,
nibColor: 'white',
optionHeight: 20,
returnFullObjects: false
};
},
componentWillMount: function() {
var value;
value = this.props.value;
this.value = value;
return this.overlayId = null;
},
componentWillUnmount: function() {
return clearInterval(this.timer);
},
render: function() {
var disabled, error, forceShowAllErrors, id, isFilter, isValid, labelField, optionItems, options, outerClass, ref2, ref3, returnFullObjects, selectText, tabIndex, value, valueField, valueHasChanged;
ref2 = this.props, options = ref2.options, id = ref2.id, selectText = ref2.selectText, valueField = ref2.valueField, labelField = ref2.labelField, tabIndex = ref2.tabIndex, disabled = ref2.disabled, isFilter = ref2.isFilter, value = ref2.value, returnFullObjects = ref2.returnFullObjects;
valueHasChanged = this.state.valueHasChanged;
ref3 = this.context.getValidationStatus(this.inputId), error = ref3.error, forceShowAllErrors = ref3.forceShowAllErrors;
isValid = error == null;
outerClass = 'field-wrap filter-select';
if (typeof wrapperClass !== "undefined" && wrapperClass !== null) {
outerClass += " " + wrapperClass;
}
if (!isValid && (valueHasChanged || forceShowAllErrors)) {
outerClass += ' invalid shrink';
}
if (value && !disabled) {
outerClass += ' x-room';
}
optionItems = [
option({
key: "none",
value: ""
}, selectText)
];
options.forEach((function(_this) {
return function(o, i) {
return optionItems.push(option({
key: i,
value: o[valueField]
}, o[labelField]));
};
})(this));
if (returnFullObjects && (value != null)) {
value = value[valueField] || '';
}
this.value = value;
return div({
className: outerClass
}, [
select({
key: 'select',
ref: 'input',
tabIndex: tabIndex,
id: id,
disabled: disabled,
value: value,
onChange: function() {},
onMouseDown: this.handleNativeEvent,
onKeyDown: this.handleNativeEvent
}, optionItems), value && !disabled ? CircleXButton({
key: 'clear',
tabIndex: -1,
ref: (function(_this) {
return function(clearBtn) {
return _this.clearBtn = clearBtn;
};
})(this),
onClick: this.clearValue
}) : void 0, div({
className: 'field-errors-show',
key: 'textInputErrorsShow',
ref: 'errorAnchor',
onMouseOver: this.handleErrorMouseOver,
onMouseOut: this.handleErrorMouseOut
})
]);
},
handleNativeEvent: function(e) {
var alphaNum, closeKeys, currentTarget, filter, isCloseKey, isOpenKey, keyCode, openKeys, options;
keyCode = e.keyCode, currentTarget = e.currentTarget;
options = this.props.options;
openKeys = [DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW, UP_ARROW, ENTER, SPACE];
closeKeys = [ESCAPE, TAB];
if (keyCode != null) {
isOpenKey = openKeys.indexOf(keyCode) > -1;
isCloseKey = closeKeys.indexOf(keyCode) > -1;
alphaNum = alphaNumericKeyCode(keyCode);
if (isOpenKey || alphaNum) {
e.preventDefault();
e.stopPropagation();
if (alphaNum) {
filter = e.key;
}
this.openOptionsList(currentTarget, filter);
} else if (isCloseKey) {
if (keyCode !== TAB) {
e.preventDefault();
}
if (this.overlayId != null) {
this.context.closeOverlay({
overlayId: this.overlayId
});
this.overlayId = null;
}
}
return;
}
e.preventDefault();
this.openOptionsList(currentTarget);
if (options.length === 0) {
return this.timer = setInterval((function(_this) {
return function() {
if (_this.props.options.length > 0) {
_this.context.closeOverlay({
overlayId: _this.overlayId
});
_this.openOptionsList(currentTarget);
return clearInterval(_this.timer);
}
};
})(this), 100);
}
},
openOptionsList: function(anchor, filter) {
var height, isFilter, labelField, nibColor, optionHeight, options, placeholder, ref2, returnFullObjects, value, valueField, width;
ref2 = this.props, value = ref2.value, options = ref2.options, labelField = ref2.labelField, valueField = ref2.valueField, height = ref2.height, width = ref2.width, placeholder = ref2.placeholder, isFilter = ref2.isFilter, nibColor = ref2.nibColor, optionHeight = ref2.optionHeight, returnFullObjects = ref2.returnFullObjects;
if (((options.length + 1) * optionHeight) < height) {
height = options.length * (optionHeight + OPTION_PADDING);
}
if (returnFullObjects && (value != null)) {
value = value[valueField] || '';
}
return this.overlayId = this.context.openOverlay({
component: Pvr,
noBackdrop: true,
id: "popover",
props: {
direction: 'below',
width: width,
height: height === 0 ? 58 : height,
anchor: anchor,
nibColor: nibColor,
enterStateStart: {
scale: 1
},
enterDuration: 50,
element: SelectInputCustomOptions({
key: 'fso',
options: this.props.options,
onChange: this.handleValueChange,
labelField: labelField,
valueField: valueField,
placeholder: placeholder,
value: value,
SelectEl: this,
isFilter: isFilter,
searchWidth: width - 10,
optionHeight: optionHeight,
OPTION_PADDING: OPTION_PADDING,
filter: filter
})
}
});
},
handleValueChange: function(value, cb) {
var jsonPath, onChange, ref2, validation, valueHasChanged;
ref2 = this.props, onChange = ref2.onChange, validation = ref2.validation, jsonPath = ref2.jsonPath;
valueHasChanged = this.state.valueHasChanged;
return this.setState({
valueHasChanged: true
}, (function(_this) {
return function() {
_this.validate(validation, value);
_this.value = value;
if (typeof onChange === "function") {
onChange(_this.getValue(), jsonPath);
}
if (typeof cb === "function") {
cb();
}
_this.focus();
return _this.fireDelayedAction();
};
})(this));
},
clearValue: function() {
return this.handleValueChange('');
}
});
module.exports = SelectInputCustom;
}).call(this);