UNPKG

ldx-widgets

Version:

widgets

361 lines (282 loc) 11.4 kB
React = require 'react' PropTypes = require 'prop-types' createClass = require 'create-react-class' assign = require 'lodash/assign' omit = require 'lodash/omit' find = require 'lodash/find' last = require 'lodash/last' moment = require 'moment' DatePicker = React.createFactory(require '../date_picker') SelectInput2 = React.createFactory(require '../select_input_2') {div, label, span} = require 'react-dom-factories' ###& @general - This component passes all props forward to datepicker, see datepicker component for it's props. @props.formLabel - OPTIONAL - [string] The value of the label that will display to the left of the input @props.className - OPTIONAL - [string] - default 'grid grid-pad' optional class to be added the main div @props.labelColumnClass - OPTIONAL - [string] optional class that will be added to the label column @props.inputColumnClass - OPTIONAL - [string] optional class that will be added to the input column @props.isFieldRequired - OPTIONAL - [boolean] optional boolean that will display the red asterisk if true @props.fullRow - OPTIONAL - [boolean] optional boolean that will determine whether to display the input in a full row with or without a label @props.enableTime - OPTIONAL - [boolean] includes the time picker @props.maxDate - OPTIONAL - [moment] when using the time picker it will disable times of day after this value @props.minDate - OPTIONAL - [moment] when using the time picker it will disable times of day before this value @props.children - OPTIONAL - [array] optional array of children &### GridFormDatePicker = createClass displayName: 'GridFormDatePicker' propTypes: formLabel: PropTypes.string className: PropTypes.string labelColumnClass: PropTypes.string inputColumnClass: PropTypes.string isFieldRequired: PropTypes.bool fullRow: PropTypes.bool enableTime: PropTypes.bool twentyFourHour: PropTypes.bool selected: PropTypes.object minutes: PropTypes.array AmPm: PropTypes.array isInPopover: PropTypes.bool disabled: PropTypes.bool onChange: PropTypes.func timeColumns: PropTypes.number returnDateString: PropTypes.string maxDate: PropTypes.object minDate: PropTypes.object getDefaultProps: -> labelColumnClass: 'col-3-12' inputColumnClass: 'col-9-12' className: 'grid grid-pad' isFieldRequired: no fullRow: yes enableTime: no twentyFourHour: no minutes: [0,15,30,45] AmPm: ['am', 'pm'] isInPopover: no disabled: no timeColumns: 2 componentDidMount: -> do @changeOnOutOfRange componentDidUpdate: -> do @changeOnOutOfRange changeOnOutOfRange: -> {selected, onChange, maxDate, minDate} = @props if selected? and (maxDate? or minDate?) # If the selected date is out of range, the controls will be updated to a value different than the passed selected date # In this case onChange must be fired to ensure the parent form has the value displayed in the form propsValue = @getMomentObject(selected) controlValue = @getMomentObject(@getValue()) do onChange if not propsValue.isSame(controlValue, 'minute') render: -> {formLabel, className, labelColumnClass, inputColumnClass, inputTextClass, isFieldRequired, selected, fullRow, enableTime, twentyFourHour, tabId, children, isInPopover, tabIndex, disabled, onChange} = @props if formLabel? labelClass = 'form-label' if isFieldRequired then labelClass += ' is-required' labelField = div { key: 'label' className: labelColumnClass }, label {className: labelClass}, formLabel # we do not want to pass the className down as it will mess up the styles inputProps = omit(assign({}, @props, {ref: 'input'}), ['className']) input = DatePicker inputProps if enableTime classes = @getCellClasses(inputColumnClass) {hour, minute, ampm} = @parseTime(selected) hourOptions = @getHourOptions {minute, ampm} inputCell = [ div { key: 'picker' className: classes[0] }, input div { key: 'time1' className: classes[1] }, [ SelectInput2 { ref: 'hour' key: 'hour' tabId: tabId wrapperClass: 'hour' value: hour or '' options: hourOptions onChange: onChange disabled: disabled isInPopover: isInPopover tabIndex: tabIndex } span { key: 'divide' className: 'time-divide' }, ':' SelectInput2 { ref: 'minute' key: 'minute' wrapperClass: 'minutes' tabId: tabId value: minute or '' options: @getMinuteOptions {hour, ampm}, hourOptions onChange: onChange disabled: disabled isInPopover: isInPopover tabIndex: tabIndex } SelectInput2 { ref: 'ampm' key: 'ampm' wrapperClass: 'ampm' tabId: tabId value: ampm or '' options: do @getAmPmOptions onChange: onChange disabled: disabled isInPopover: isInPopover tabIndex: tabIndex } unless twentyFourHour ] ] else inputCell = div { key: 'input' className: inputColumnClass }, input # This is a full row of a form with a label if fullRow and formLabel? content = [ labelField inputCell ].concat children div { className: className }, content # This is a full row w/ out a label else if fullRow content = [ inputCell ].concat children div { className: className }, content # This is a single cell within a row else inputCell getValue: -> {enableTime, returnDateString, twentyFourHour} = @props timestamp = @refs.input.getValue() return null unless timestamp? return timestamp unless enableTime minute = @refs.minute.getValue() hour = @refs.hour.getValue() ampm = @refs.ampm?.getValue() or '' # When returnDateString is passed the datepicker will return a string instead of a moment object # in this case momentize it before extracting the time if returnDateString? date = moment(timestamp, returnDateString).format('YYYYMMDD') rv = moment("#{date}#{hour}#{minute}#{ampm}", if twentyFourHour then 'YYYYMMDDHHmm' else 'YYYYMMDDhmma') rv.format(returnDateString) else date = timestamp.format('YYYYMMDD') moment("#{date}#{hour}#{minute}#{ampm}", if twentyFourHour then 'YYYYMMDDHHmm' else 'YYYYMMDDhmma') getMomentObject: (date) -> {returnDateString} = @props # When returnDateString is passed the datepicker will return a string instead of a moment object # in this case momentize it before extracting the time if returnDateString? then moment(date, returnDateString) else date parseTime: (timestamp) -> return {} unless timestamp? {twentyFourHour, returnDateString} = @props timestamp = @getMomentObject timestamp { hour: timestamp.format(if twentyFourHour then 'HH' else 'h') minute: timestamp.format('mm') ampm: timestamp.format('a') } getHourOptions: (parsedTime)-> {twentyFourHour} = @props hoursArray = if twentyFourHour then [0..23] else [12].concat [1..11] hourOptions = ({ label: if twentyFourHour then @prependZero hour else "#{hour}" value: if twentyFourHour then @prependZero hour else "#{hour}" disabled: no } for hour in hoursArray) @removeOutOfRange 'hours', parsedTime, hourOptions hourOptions getMinuteOptions: (parsedTime, hourOptions) -> {minutes} = @props minuteOptions = ({ label: @prependZero minute value: @prependZero minute disabled: no } for minute in minutes) @removeOutOfRange 'minutes', parsedTime, minuteOptions, hourOptions minuteOptions getAmPmOptions: -> {AmPm} = @props ampmOptions = ({ label: opt value: opt } for opt in AmPm) @removeOutOfRangeAmPm ampmOptions ampmOptions removeOutOfRange: (timeSection, parsedTime, optionsList, hourOptions = []) -> {selected, twentyFourHour, minDate, maxDate, minutes} = @props dateFormat = if twentyFourHour then 'MMDDYYYY HHmm' else 'MMDDYYYY hmma' if selected? currentValue = @getMomentObject selected # If the selected hour is out of range, use the first hour option for checking the mintes selectedHour = if not find(hourOptions, {value: parsedTime.hour})? then (hourOptions[0]?.value or '') else parsedTime.hour # When checking for the hours to include in the list, use the highest/lowest minute option # This will prevent and hour option from appearing that has no corresponding minute options # It will also ensure the max hours is in the list if there is a corresponding minute that puts it in range lastMinute = @prependZero last(minutes) firstMinute = @prependZero minutes[0] # When the max date is selected, remove hours and minutes that fall after the max time if maxDate? and currentValue.isSame(maxDate, 'day') maxDay = maxDate.format('MMDDYYYY') for option, o in optionsList by -1 checkTime = switch timeSection when 'hours' then moment("#{maxDay} #{option.value}#{firstMinute}#{if twentyFourHour then '' else parsedTime.ampm}", dateFormat) when 'minutes' then moment("#{maxDay} #{selectedHour}#{option.value}#{if twentyFourHour then '' else parsedTime.ampm}", dateFormat) optionsList.splice(o, 1) if checkTime.isAfter(maxDate, 'minute') # When the min date is selected, remove hours and minutes that fall before the min time if minDate? and currentValue.isSame(minDate, 'day') minDay = minDate.format('MMDDYYYY') for option, o in optionsList by -1 checkTime = switch timeSection when 'hours' then moment("#{minDay} #{option.value}#{lastMinute}#{if twentyFourHour then '' else parsedTime.ampm}", dateFormat) when 'minutes' then moment("#{minDay} #{selectedHour}#{option.value}#{if twentyFourHour then '' else parsedTime.ampm}", dateFormat) optionsList.splice(o, 1) if checkTime.isBefore(minDate, 'minute') return optionsList removeOutOfRangeAmPm: (optionsList) -> {selected, minDate, maxDate} = @props if selected? currentValue = @getMomentObject selected if maxDate? if currentValue.isSame(maxDate, 'day') if maxDate.format('a') is 'am' then optionsList.splice 1, 1 if minDate? if currentValue.isSame(minDate, 'day') if minDate.format('a') is 'pm' then optionsList.splice 0, 1 getCellClasses: (inputColumnClass) -> {timeColumns} = @props classSplit = inputColumnClass.split('-') dateCol = classSplit[1] colCount = classSplit[2] [ "col-#{dateCol - timeColumns}-#{colCount}" "col-#{timeColumns}-#{colCount}" ] prependZero: (value) -> if +value < 10 then "0#{value}" else "#{value}" module.exports = GridFormDatePicker