UNPKG

ldx-widgets

Version:

widgets

256 lines (207 loc) 6.92 kB
React = require 'react' createClass = require 'create-react-class' PropTypes = require 'prop-types' _ = require 'lodash' { DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW, UP_ARROW, ENTER, SPACE, ESCAPE, TAB } = require '../constants/keyboard' {alphaNumericKeyCode} = require '../utils' FormValidation = require '../mixins/form_validation' CircleXButton = React.createFactory(require './circle_x_button') SelectInputCustomOptions = React.createFactory(require './select_input_custom_options') {div, select, option} = React.DOM ###& @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: [FormValidation] contextTypes: openOverlay: PropTypes.func closeOverlay: 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: -> options: [] selectText: 'Select from list...' placeholder: 'Filter options' valueField: 'value' labelField: 'title' width: 250 height: 400 isFilter: no nibColor: 'white' optionHeight: 20 getInitialState: -> {value, valueField} = @props val = '' if typeof value is 'object' then val = value[valueField] else val = value return { value: val } render: -> { options, id, selectText, valueField, labelField, tabIndex, disabled, isFilter } = @props { value } = @state optionItems = [] # Populate an initial value optionItems.push option { key: "none" value: "" }, selectText options.forEach (o, i) => optionItems.push option { key: i value: o[valueField] }, o[labelField] @getErrors() roomForCircleXStyle = if value and not disabled then "x-room" else "" div { className: "field-wrap filter-select #{@invalidClass} #{roomForCircleXStyle}" }, [ select { key: 'select' ref: (control) => @control = control tabIndex: tabIndex id: id disabled: disabled value: value onMouseDown: @handleNativeEvent onKeyDown: @handleNativeEvent }, optionItems CircleXButton { key: 'clear' tabIndex: tabIndex ref: (clearBtn) => @clearBtn = clearBtn onClick: @clear } if value and not disabled div({ className: 'field-errors-show' key: 'errors' }, [ div { className: 'field-errors' key: 'err' }, ul { className: 'field-errors-list' }, @validationErrors ]) if @validationErrors.length ] handleNativeEvent: (e) -> { keyCode } = e { options, selectText, labelField, valueField, height, width, placeholder, isFilter, nibColor, optionHeight } = @props { value } = @state openKeys = [DOWN_ARROW, RIGHT_ARROW, LEFT_ARROW, UP_ARROW, ENTER, SPACE] closeKeys = [ESCAPE, TAB] if ((options.length + 1) * optionHeight) < height height = (options.length + 2) * optionHeight # Construct the options component overlay = activeComponent: 'pvr' close: @context.closeOverlay direction: 'below' element: key: 'fso' options: options onChange: @handleChange labelField: labelField valueField: valueField placeholder: placeholder value: value SelectEl: @ isFilter: isFilter width: width height: height anchor: e.currentTarget noBackdrop: true nibColor: nibColor optionHeight: optionHeight # Handle the keyboard if keyCode? isOpenKey = openKeys.indexOf(keyCode) > -1 isCloseKey = closeKeys.indexOf(keyCode) > -1 alphaNum = alphaNumericKeyCode(keyCode) if isOpenKey or alphaNum e.preventDefault() e.stopPropagation() # For alphanumeric input, start filtering the element if alphaNum then overlay.element.filter = e.key # Set the element to the component, replacing the pojo overlay.element = SelectInputCustomOptions(overlay.element) # Open the overlay @context.openOverlay(overlay) else if isCloseKey if keyCode isnt TAB e.preventDefault() @context.closeOverlay() return # Set the element to the component, replacing the pojo overlay.element = SelectInputCustomOptions(overlay.element) # Handle mouse clicks e.preventDefault() @context.openOverlay(overlay) handleChange: (value, cb) -> @setState value: value , => @props.onChange(value) @focus() cb?() clear: -> @handleChange('') focus: -> @control.focus(); getValue: -> {returnFullObjects, valueField} = @props if returnFullObjects return _.find @props.options, (o) => v = o[valueField] if typeof v is 'number' then v = v.toString() return v is @control.value else return @control.value module.exports = SelectInputCustom