diff --git a/docs/src/app/app-routes.jsx b/docs/src/app/app-routes.jsx index 65546a67fb9252..7228c2a99e5518 100644 --- a/docs/src/app/app-routes.jsx +++ b/docs/src/app/app-routes.jsx @@ -31,6 +31,7 @@ var Snackbar = require('./components/pages/components/snackbar.jsx'); var Switches = require('./components/pages/components/switches.jsx'); var Tabs = require('./components/pages/components/tabs.jsx'); var TextFields = require('./components/pages/components/text-fields.jsx'); +var TimePicker = require('./components/pages/components/time-picker.jsx'); var Toolbars = require('./components/pages/components/toolbars.jsx'); @@ -70,6 +71,7 @@ var AppRoutes = ( + diff --git a/docs/src/app/components/pages/components.jsx b/docs/src/app/components/pages/components.jsx index 035e8855b83a73..8fee274f2a3568 100644 --- a/docs/src/app/components/pages/components.jsx +++ b/docs/src/app/components/pages/components.jsx @@ -20,6 +20,7 @@ class Components extends React.Component { { route: 'snackbar', text: 'Snackbar'}, { route: 'tabs', text: 'Tabs'}, { route: 'text-fields', text: 'Text Fields'}, + { route: 'time-picker', text: 'Time Picker'}, { route: 'toolbars', text: 'Toolbars'}, ]; diff --git a/docs/src/app/components/pages/components/time-picker.jsx b/docs/src/app/components/pages/components/time-picker.jsx new file mode 100644 index 00000000000000..df390dea1b5fca --- /dev/null +++ b/docs/src/app/components/pages/components/time-picker.jsx @@ -0,0 +1,85 @@ +var React = require('react'); +var mui = require('mui'); +var TimePicker = mui.TimePicker; +var ComponentDoc = require('../../component-doc.jsx'); + +var TimePickerPage = React.createClass({ + + render: function() { + + var code = + '//The 12hr format \n' + + '\n\n' + + '//The 24hr format \n' + + ' '; + + var componentInfo = [ + { + name: 'Props', + infoArray: [ + { + name: 'defaultTime', + type: 'date object', + header: 'optional', + desc: 'This is the initial time value of the component.' + }, + { + name: 'format', + type: 'one of: ampm, 24hr', + header: 'default: ampm', + desc: 'Tells the component to display the picker in ampm (12hr) format or 24hr format.' + } + ] + }, + { + name: 'Methods', + infoArray: [ + { + name: 'getTime', + header: 'DatePicker.getTime()', + desc: 'Returns the current time value.' + }, + { + name: 'setTime', + header: 'DatePicker.setTime(t)', + desc: 'Sets the time value to t, where t is a date object.' + } + ] + } + ]; + + return ( + + + + + + + + ); + }, + _changeTimePicker24: function(err, t){ + this.refs.picker24hr.setTime(t); + }, + _changeTimePicker12: function(err, t){ + this.refs.picker12hr.setTime(t); + } + +}); + +module.exports = TimePickerPage; \ No newline at end of file diff --git a/src/index.js b/src/index.js index 0945523659a682..73c727b5445209 100644 --- a/src/index.js +++ b/src/index.js @@ -44,6 +44,7 @@ module.exports = { Tab: require('./tabs/tab'), Tabs: require('./tabs/tabs'), Toggle: require('./toggle'), + TimePicker: require('./time-picker'), TextField: require('./text-field'), Toolbar: require('./toolbar/toolbar'), ToolbarGroup: require('./toolbar/toolbar-group'), diff --git a/src/styles/themes/light-theme.js b/src/styles/themes/light-theme.js index cd1e192841f511..7bbac60502aa2b 100644 --- a/src/styles/themes/light-theme.js +++ b/src/styles/themes/light-theme.js @@ -122,6 +122,14 @@ var LightTheme = { backgroundColor: '#323232', actionColor: palette.accent1Color }, + timePicker: { + color: Colors.white, + textColor: Colors.grey600, + accentColor: palette.primary1Color, + clockColor: Colors.black, + selectColor: palette.primary2Color, + selectTextColor: Colors.white + }, toggle: { thumbOnColor: palette.primary1Color, thumbOffColor: Colors.grey50, diff --git a/src/time-picker/clock-button.jsx b/src/time-picker/clock-button.jsx new file mode 100644 index 00000000000000..baf01842aff82d --- /dev/null +++ b/src/time-picker/clock-button.jsx @@ -0,0 +1,96 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); +var EnhancedButton = require('../enhanced-button'); +var Transitions = require('../styles/transitions'); + +var ClockButton = React.createClass({ + + mixins: [StylePropable], + + contextTypes: { + theme: React.PropTypes.object + }, + + propTypes: { + position: React.PropTypes.oneOf(['left', 'right']) + }, + + getDefaultProps: function () { + return { + position: "left" + }; + }, + _handleTouchTap: function(){ + + this.setState({ + selected: true + }) + this.props.onTouchTap(); + }, + getTheme: function() { + return this.context.theme.component.timePicker; + }, + render: function() { + + var { + className, + ...other} = this.props; + + var styles = { + root: { + position: "absolute", + bottom: "65px", + pointerEvents: "auto", + height: "50px", + width: "50px", + borderRadius: "100%" + }, + + label : { + position: "absolute", + top: "17px", + left: "14px" + }, + + select: { + position: 'absolute', + height: 50, + width: 50, + top: "0px", + left: "0px", + opacity: 0, + borderRadius: '50%', + transform: 'scale(0)', + transition: Transitions.easeOut(), + backgroundColor: this.getTheme().accentColor, + }, + }; + + if (this.props.selected) { + styles.label.color = this.getTheme().selectTextColor; + styles.select.opacity = 1; + styles.select.transform = 'scale(1)'; + } + + if( this.props.position == "right" ){ + styles.root.right = "5px"; + }else{ + styles.root.left = "5px"; + } + + + + return ( + + + {this.props.children} + + ); + } +}); + +module.exports = ClockButton; \ No newline at end of file diff --git a/src/time-picker/clock-hours.jsx b/src/time-picker/clock-hours.jsx new file mode 100644 index 00000000000000..3cbd5a58c063fa --- /dev/null +++ b/src/time-picker/clock-hours.jsx @@ -0,0 +1,178 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); +var ClockNumber = require("./clock-number"); +var ClockPointer = require("./clock-pointer"); + +function rad2deg(rad){ + return rad * 57.29577951308232 +} + +var ClockHours = React.createClass({ + + mixins: [StylePropable], + + propTypes: { + initialHours: React.PropTypes.number, + onChange: React.PropTypes.func, + format: React.PropTypes.oneOf(['ampm', '24hr']) + }, + + center: {x: 0, y: 0}, + basePoint: {x: 0, y: 0}, + isMousePressed: function(e){ + + if(typeof e.buttons == "undefined"){ + return e.nativeEvent.which; + } + + return e.buttons; + + }, + getDefaultProps: function() { + return { + initialHours: new Date().getHours(), + onChange: function(){}, + format: 'ampm' + }; + }, + + componentDidMount: function () { + var clockElement = this.refs.mask.getDOMNode(); + + this.center = { + x: clockElement.offsetWidth / 2, + y: clockElement.offsetHeight / 2, + }; + + this.basePoint = { + x: this.center.x, + y: 0 + }; + }, + handleUp: function(e){ + e.preventDefault(); + this.setClock(e, true); + }, + handleMove: function(e){ + e.preventDefault(); + if(this.isMousePressed(e) != 1 ) return; + this.setClock(e, false); + }, + handleTouch: function(e){ + e.preventDefault(); + this.setClock(e, false); + }, + setClock: function(e, finish){ + + + var pos = { + x: e.nativeEvent.offsetX, + y: e.nativeEvent.offsetY + }; + + var hours = this.getHours(pos.x, pos.y); + + this.props.onChange(hours, finish); + + }, + getHours: function(x, y){ + + var step = 30; + x = x - this.center.x; + y = y - this.center.y; + var cx = this.basePoint.x - this.center.x; + var cy = this.basePoint.y - this.center.y; + + var atan = Math.atan2(cx, cy) - Math.atan2(x, y); + + var deg = rad2deg(atan); + deg = Math.round(deg / step ) * step; + deg %= 360; + + var value = Math.floor(deg / step) || 0; + + var delta = Math.pow(x, 2) + Math.pow(y, 2); + var distance = Math.sqrt(delta); + + value = value || 12; + if(this.props.format == "24hr"){ + if(distance < 90){ + value += 12; + value %= 24; + } + }else{ + value %= 12; + } + + return value; + + }, + _getSelected: function(){ + + var hour = this.props.initialHours; + + if(this.props.format == "ampm"){ + hour %= 12; + hour = hour || 12; + } + + return hour; + }, + _getHourNumbers: function(){ + var style = { + pointerEvents: "none" + }; + + var hourSize = this.props.format == 'ampm' ? 12 : 24; + + var hours = []; + + for(var i = 1; i <= hourSize; i++){ + hours.push(i % 24); + } + + return hours.map(function(hour){ + + var isSelected = this._getSelected() == hour; + return ; + + }.bind(this)); + + }, + + render: function() { + + var styles = { + root: { + height: "100%", + width: "100%", + borderRadius: "100%", + position: "relative", + pointerEvents: "none", + boxSizing: "border-box", + }, + + hitMask: { + height: "100%", + width: "100%", + pointerEvents: "auto", + }, + + }; + + + var hours = this._getSelected(); + var numbers = this._getHourNumbers(); + + return ( +
+ + {numbers} +
+
+ ); + } +}); + +module.exports = ClockHours; + \ No newline at end of file diff --git a/src/time-picker/clock-minutes.jsx b/src/time-picker/clock-minutes.jsx new file mode 100644 index 00000000000000..e03f8582459144 --- /dev/null +++ b/src/time-picker/clock-minutes.jsx @@ -0,0 +1,161 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); + +var ClockNumber = require("./clock-number"); +var ClockPointer = require("./clock-pointer"); + +function rad2deg(rad){ + return rad * 57.29577951308232 +} + +var ClockMinutes = React.createClass({ + + mixins: [StylePropable], + + contextTypes: { + theme: React.PropTypes.object + }, + + propTypes: { + initialMinutes: React.PropTypes.number, + onChange: React.PropTypes.func + }, + + center: {x: 0, y: 0}, + basePoint: {x: 0, y: 0}, + isMousePressed: function(e){ + + if(typeof e.buttons == "undefined"){ + return e.nativeEvent.which; + } + return e.buttons; + + }, + getDefaultProps: function() { + return { + initialMinutes: new Date().getMinutes(), + onChange: function(){} + }; + }, + + componentDidMount: function () { + var clockElement = this.refs.mask.getDOMNode(); + + this.center = { + x: clockElement.offsetWidth / 2, + y: clockElement.offsetHeight / 2, + }; + + this.basePoint = { + x: this.center.x, + y: 0 + }; + + }, + handleUp: function(e){ + e.preventDefault(); + this.setClock(e, true); + }, + handleMove: function(e){ + e.preventDefault(); + if(this.isMousePressed(e) != 1 ) return; + this.setClock(e, false); + }, + handleTouch: function(e){ + e.preventDefault(); + this.setClock(e, false); + }, + setClock: function(e, finish){ + + e.preventDefault(); + if(this.isMousePressed(e) != 1 ) return; + + var pos = { + x: e.nativeEvent.offsetX, + y: e.nativeEvent.offsetY + }; + + var minutes = this.getMinutes(pos.x, pos.y) + + this.props.onChange(minutes, finish); + + }, + getMinutes: function(x, y){ + + var step = 6; + x = x - this.center.x; + y = y - this.center.y; + var cx = this.basePoint.x - this.center.x; + var cy = this.basePoint.y - this.center.y; + + var atan = Math.atan2(cx, cy) - Math.atan2(x, y); + + var deg = rad2deg(atan); + deg = Math.round(deg / step ) * step; + deg %= 360; + + var value = Math.floor(deg / step) || 0; + + return value; + + }, + _getMinuteNumbers: function(){ + + var minutes = []; + for(var i = 0; i < 12; i++){ + minutes.push(i * 5); + } + var selectedMinutes = this.props.initialMinutes; + + + var hasSelected = false; + + var numbers = minutes.map(function(minute){ + var isSelected = selectedMinutes == minute; + if(isSelected) hasSelected = true; + return ; + }.bind(this)); + + return { + numbers: numbers, + hasSelected: hasSelected, + selected: selectedMinutes + } + + }, + render: function() { + + + var styles = { + root: { + height: "100%", + width: "100%", + borderRadius: "100%", + position: "relative", + pointerEvents: "none", + boxSizing: "border-box", + }, + + hitMask: { + height: "100%", + width: "100%", + pointerEvents: "auto", + }, + + }; + + var minutes = this._getMinuteNumbers(); + + + return ( +
+ + {minutes.numbers} +
+
+ ); + } +}); + +module.exports = ClockMinutes; + \ No newline at end of file diff --git a/src/time-picker/clock-number.jsx b/src/time-picker/clock-number.jsx new file mode 100644 index 00000000000000..9b33209993737c --- /dev/null +++ b/src/time-picker/clock-number.jsx @@ -0,0 +1,116 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); + +var ClockNumber = React.createClass({ + + mixins: [StylePropable], + + contextTypes: { + theme: React.PropTypes.object + }, + + propTypes: { + value: React.PropTypes.number, + type: React.PropTypes.oneOf(['hour', 'minute']), + onSelected: React.PropTypes.func, + isSelected: React.PropTypes.bool + }, + getDefaultProps: function() { + return { + value: 0, + type: 'minute', + isSelected: false + }; + }, + getTheme: function() { + return this.context.theme.component.timePicker; + }, + render: function() { + + var pos = this.props.value; + + var inner = false; + + if(this.props.type == "hour"){ + inner = pos < 1 || pos > 12; + pos %= 12; + }else{ + pos = pos / 5; + } + + var positions = [ + [0, 5], + [54.5, 16.6], + [94.4, 59.5], + [109, 114], + [94.4, 168.5], + [54.5, 208.4], + [0, 223], + [-54.5, 208.4], + [-94.4, 168.5], + [-109, 114], + [-94.4, 59.5], + [-54.5, 19.6] + ]; + + var innerPositions = [ + [0, 40], + [36.9, 49.9], + [64, 77], + [74, 114], + [64, 151], + [37, 178], + [0, 188], + [-37, 178], + [-64, 151], + [-74, 114], + [-64, 77], + [-37, 50] + ]; + + var styles = { + root: { + display: "inline-block", + position: "absolute", + width: "32px", + height: "32px", + borderRadius: "100%", + left: 'calc(50% - 16px)', + top: "10px", + textAlign: "center", + paddingTop: '5px', + userSelect: "none", /* Chrome all / Safari all */ + fontSize: "1.1em", + pointerEvents: "none", + boxSizing: "border-box", + } + + } + + if(this.props.isSelected){ + styles.root.backgroundColor = this.getTheme().accentColor; + styles.root.color = this.getTheme().selectTextColor; + } + + var transformPos = positions[pos]; + + if(inner){ + styles.root.width = "28px"; + styles.root.height = "28px"; + styles.root.left = 'calc(50% - 14px)'; + transformPos = innerPositions[pos]; + } + + var [x, y] = transformPos; + + styles.root.transform = "translate(" + x + "px, " + y + "px)"; + + + + return ( + {this.props.value} + ); + } +}); + +module.exports = ClockNumber; \ No newline at end of file diff --git a/src/time-picker/clock-pointer.jsx b/src/time-picker/clock-pointer.jsx new file mode 100644 index 00000000000000..1ab13566a37248 --- /dev/null +++ b/src/time-picker/clock-pointer.jsx @@ -0,0 +1,108 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); + +var ClockPointer = React.createClass({ + + mixins: [StylePropable], + + contextTypes: { + theme: React.PropTypes.object + }, + + propTypes: { + value: React.PropTypes.number, + type: React.PropTypes.oneOf(['hour', 'minute']) + }, + + getInitialState: function () { + return { + inner: this.isInner(this.props.value) + }; + }, + getDefaultProps: function() { + return { + value: null, + type: 'minute', + hasSelected: false + }; + }, + componentWillReceiveProps: function (nextProps) { + + this.setState({ + inner: this.isInner(nextProps.value) + }); + }, + isInner: function(value){ + if(this.props.type != "hour" ) { + return false; + } + return value < 1 || value > 12 ; + }, + getAngle: function(){ + + if(this.props.type == "hour"){ + return this.calcAngle(this.props.value, 12); + } + + return this.calcAngle(this.props.value, 60); + + }, + calcAngle: function(value, base){ + value %= base; + var angle = 360 / base * value; + return angle; + + }, + getTheme: function() { + return this.context.theme.component.timePicker; + }, + render: function() { + + if(this.props.value == null){ + return ; + } + + var angle = this.getAngle(); + + var styles = { + root: { + height: "30%", + background: this.getTheme().accentColor, + width: "2px", + left: 'calc(50% - 1px)', + position: "absolute", + bottom: "50%", + transformOrigin: "bottom", + pointerEvents: "none", + transform: "rotateZ(" + angle + "deg)" + }, + mark: { + background: this.getTheme().selectTextColor, + border: "4px solid " + this.getTheme().accentColor, + width: "7px", + height: "7px", + position: "absolute", + top: "-5px", + left: "-6px", + borderRadius: "100%", + } + }; + + + if(!this.state.inner ){ + styles.root.height = "40%"; + }; + + if(this.props.hasSelected){ + styles.mark.display = "none"; + } + + return ( +
+
+
+ ); + } +}); + +module.exports = ClockPointer; diff --git a/src/time-picker/clock.jsx b/src/time-picker/clock.jsx new file mode 100644 index 00000000000000..2d6a959eba0499 --- /dev/null +++ b/src/time-picker/clock.jsx @@ -0,0 +1,180 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); + +var TimeDisplay = require("./time-display"); +var ClockButton = require("./clock-button"); +var ClockHours = require("./clock-hours"); +var ClockMinutes = require("./clock-minutes"); +var SlideInTransitionGroup = require('../transition-groups/slide-in'); + +var Clock = React.createClass({ + + mixins: [StylePropable], + + propTypes: { + initialTime: React.PropTypes.object, + mode: React.PropTypes.oneOf(['hour', 'minute']), + format: React.PropTypes.oneOf(['ampm', '24hr']), + isActive: React.PropTypes.bool + }, + + init: function(){ + this.setState({ + mode: 'hour' + }) + }, + + getDefaultProps: function() { + return { + initialTime: new Date() + }; + }, + + componentWillReceiveProps: function (nextProps) { + + this.setState({ + selectedTime: nextProps.initialTime + }); + }, + + getInitialState: function() { + + return { + selectedTime: this.props.initialTime, + mode: 'hour' + }; + }, + + + _setMode: function(mode){ + setTimeout(function(){ + this.setState({ + mode: mode + }) + }.bind(this), 100); + }, + + _setAffix: function(affix){ + + if(affix == this._getAffix()) return; + + var hours = this.state.selectedTime.getHours(); + + if(affix == "am"){ + this.handleChangeHours(hours - 12); + return; + } + + this.handleChangeHours(hours + 12); + }, + + _getAffix: function(){ + + if(this.props.format != "ampm") return ""; + + var hours = this.state.selectedTime.getHours(); + if(hours < 12){ + return "am"; + } + + return "pm"; + + }, + + _getButtons: function(){ + var buttons = []; + var isAM = this._getIsAM(); + + if(this.props.format == 'ampm'){ + buttons = [ + {"AM"}, + {"PM"} + ]; + } + return buttons; + }, + + _getIsAM: function(){ + + return this._getAffix() == "am"; + + }, + + render: function() { + + var clock = null; + var buttons = this._getButtons(); + + var styles = { + root: {}, + + container: { + height: "280px", + padding: "10px", + } + }; + + + + if( this.state.mode == "hour"){ + clock = + }else{ + clock = + + } + + + return ( +
+ + + +
+ {clock} +
+ {buttons} +
+ ); + }, + handleChangeHours: function(hours, finished){ + var time = new Date(this.state.selectedTime); + + time.setHours(hours); + this.setState({ + selectedTime: time + }); + + if(finished){ + setTimeout(function(){ + this.setState({ + mode: 'minute' + }) + }.bind(this), 100); + } + }, + handleChangeMinutes: function(minutes){ + var time = new Date(this.state.selectedTime); + time.setMinutes(minutes); + this.setState({ + selectedTime: time + }); + }, + getSelectedTime: function(){ + return this.state.selectedTime; + } +}); + +module.exports = Clock; \ No newline at end of file diff --git a/src/time-picker/index.js b/src/time-picker/index.js new file mode 100644 index 00000000000000..f29730378e1b9e --- /dev/null +++ b/src/time-picker/index.js @@ -0,0 +1 @@ +module.exports = require('./time-picker'); \ No newline at end of file diff --git a/src/time-picker/time-display.jsx b/src/time-picker/time-display.jsx new file mode 100644 index 00000000000000..f390dc86fcac4d --- /dev/null +++ b/src/time-picker/time-display.jsx @@ -0,0 +1,130 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); + +var SlideInTransitionGroup = require('../transition-groups/slide-in'); + +var TimeDisplay = React.createClass({ + + mixins: [StylePropable], + + contextTypes: { + theme: React.PropTypes.object + }, + + propTypes: { + selectedTime: React.PropTypes.object.isRequired, + format: React.PropTypes.oneOf(['ampm', '24hr']), + mode: React.PropTypes.oneOf(['hour', 'minute']), + affix: React.PropTypes.oneOf(['', 'pm', 'am']) + }, + + getInitialState: function() { + return { + transitionDirection: 'up' + }; + }, + getDefaultProps: function () { + return { + mode: 'hour' , + affix: '' + }; + }, + componentWillReceiveProps: function(nextProps) { + var direction; + + if (nextProps.selectedTime !== this.props.selectedTime) { + direction = nextProps.selectedTime > this.props.selectedTime ? 'up' : 'down'; + this.setState({ + transitionDirection: direction + }); + } + }, + sanitizeTime: function(time){ + var hour = this.props.selectedTime.getHours(); + var min = this.props.selectedTime.getMinutes().toString(); + + if(this.props.format == "ampm"){ + + hour %= 12; + hour = hour || 12; + } + + hour = hour.toString(); + if(hour.length < 2 ) hour = "0" + hour; + if(min.length < 2 ) min = "0" + min; + + return [hour, min]; + }, + getTheme: function() { + return this.context.theme.component.timePicker; + }, + render: function() { + + var { + selectedTime, + mode, + ...other + } = this.props; + + var styles = { + root: { + textAlign: "center", + position: "relative", + width: "280px", + height: "100%", + }, + + time: { + margin: "6px 0", + lineHeight: "58px", + height: "58px", + fontSize: "58px", + }, + + box: { + padding: "16px 0", + backgroundColor: this.getTheme().color, + color: this.getTheme().textColor, + }, + + + hour: {}, + + minute: {} + } + + + var [hour, min] = this.sanitizeTime(); + + + var hourClassName = ""; + var minClassName = ""; + + styles[mode].color = this.getTheme().accentColor; + + return ( +
+ +
+ + +
+ {hour} + : + {min} +
+ {this.props.affix.toUpperCase()} + + +
+ +
+ ); + } + +}); + +module.exports = TimeDisplay; + + + \ No newline at end of file diff --git a/src/time-picker/time-picker-dialog.jsx b/src/time-picker/time-picker-dialog.jsx new file mode 100644 index 00000000000000..6365e133613c2a --- /dev/null +++ b/src/time-picker/time-picker-dialog.jsx @@ -0,0 +1,127 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); +var WindowListenable = require('../mixins/window-listenable'); +var KeyCode = require('../utils/key-code'); +var Clock = require('./clock'); +var DialogWindow = require('../dialog-window'); +var FlatButton = require('../flat-button'); + +var TimePickerDialog = React.createClass({ + + mixins: [StylePropable, WindowListenable], + + contextTypes: { + theme: React.PropTypes.object + }, + + propTypes: { + initialTime: React.PropTypes.object, + onAccept: React.PropTypes.func, + onShow: React.PropTypes.func, + onDismiss: React.PropTypes.func, + }, + + windowListeners: { + 'keyup': '_handleWindowKeyUp' + }, + + + getTheme: function() { + return this.context.theme.component.timePicker; + }, + render: function() { + var { + initialTime, + onAccept, + format, + ...other + } = this.props; + + var styles = { + root: { + fontSize: "14px", + color: this.getTheme().clockColor, + }, + dialogContent: { + width: "280px", + } + }; + + var actions = [ + , + + ]; + + return ( + + + + ); + }, + + show: function() { + this.refs.dialogWindow.show(); + this.refs.clock.init(); + }, + + dismiss: function() { + this.refs.dialogWindow.dismiss(); + }, + + _handleCancelTouchTap: function() { + this.dismiss(); + }, + + _handleOKTouchTap: function() { + this.dismiss(); + if (this.props.onAccept) { + this.props.onAccept(this.refs.clock.getSelectedTime()); + } + }, + + _handleDialogShow: function() { + + if(this.props.onShow) { + this.props.onShow(); + } + }, + + _handleDialogDismiss: function() { + + + if(this.props.onDismiss) { + this.props.onDismiss(); + } + }, + + _handleWindowKeyUp: function(e) { + if (this.refs.dialogWindow.isOpen()) { + switch (e.keyCode) { + case KeyCode.ENTER: + this._handleOKTouchTap(); + break; + } + } + } + +}); + +module.exports = TimePickerDialog; \ No newline at end of file diff --git a/src/time-picker/time-picker.jsx b/src/time-picker/time-picker.jsx new file mode 100644 index 00000000000000..8dc18781a0fe22 --- /dev/null +++ b/src/time-picker/time-picker.jsx @@ -0,0 +1,135 @@ +var React = require('react'); +var StylePropable = require('../mixins/style-propable'); + +var WindowListenable = require('../mixins/window-listenable'); +var DateTime = require('../utils/date-time'); +var KeyCode = require('../utils/key-code'); +var TimePickerDialog = require('./time-picker-dialog'); +var TextField = require('../text-field'); + +var emptyTime = new Date(); +emptyTime.setHours(0); +emptyTime.setMinutes(0); + +var TimePicker = React.createClass({ + + mixins: [StylePropable, WindowListenable], + + propTypes: { + defaultTime: React.PropTypes.object, + format: React.PropTypes.oneOf(['ampm', '24hr']), + onFocus: React.PropTypes.func, + onTouchTap: React.PropTypes.func, + onChange: React.PropTypes.func, + onShow: React.PropTypes.func, + onDismiss: React.PropTypes.func, + }, + + windowListeners: { + 'keyup': '_handleWindowKeyUp' + }, + + getDefaultProps: function() { + return { + defaultTime: emptyTime, + format: 'ampm' + }; + }, + + getInitialState: function() { + return { + time: this.props.defaultTime, + dialogTime: new Date() + }; + }, + formatTime: function(date){ + + var hours = date.getHours(); + var mins = date.getMinutes(); + var aditional = ""; + + if(this.props.format == "ampm"){ + var isAM = hours < 12; + hours = hours % 12; + aditional += isAM ? " am" : " pm"; + hours = hours || 12; + } + + hours = hours.toString(); + mins = mins.toString(); + + if(hours.length < 2) hours = "0" + hours; + if(mins.length < 2) mins = "0" + mins; + + return hours + ":" + mins + aditional; + }, + render: function() { + var { + format, + onFocus, + onTouchTap, + onShow, + onDismiss, + ...other + } = this.props; + + var defaultInputValue; + + if (this.props.defaultDate) { + defaultInputValue = this.formatTime(this.props.defaultTime); + } + + return ( +
+ + +
+ + ); + }, + + getTime: function() { + return this.state.time; + }, + + setTime: function(t) { + this.setState({ + time: t + }); + this.refs.input.setValue(this.formatTime(t)); + }, + + _handleDialogAccept: function(t) { + + this.setTime(t); + if (this.props.onChange) this.props.onChange(null, t); + }, + + _handleInputFocus: function(e) { + e.target.blur(); + if (this.props.onFocus) this.props.onFocus(e); + }, + + _handleInputTouchTap: function(e) { + this.setState({ + dialogTime: this.getTime() + }); + + this.refs.dialogWindow.show(); + if (this.props.onTouchTap) this.props.onTouchTap(e); + } + +}); + +module.exports = TimePicker;