commit
4ba3e005a8
|
@ -3,6 +3,7 @@ FontIcon = require '../font_icon'
|
|||
Ripple = require '../ripple'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Button'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
css = require './style'
|
||||
dateUtils = require '../date_utils'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'Day',
|
||||
|
||||
propTypes:
|
||||
day : React.PropTypes.number
|
||||
onClick : React.PropTypes.func
|
||||
selectedDate : React.PropTypes.object
|
||||
viewDate : React.PropTypes.object
|
||||
|
||||
_dayStyle: ->
|
||||
marginLeft: "#{dateUtils.firstWeekDay(@props.viewDate) * 100/7}%"
|
||||
|
||||
_isSelected: () ->
|
||||
isSameYear = @props.viewDate.getFullYear() == @props.selectedDate.getFullYear()
|
||||
isSameMonth = @props.viewDate.getMonth() == @props.selectedDate.getMonth()
|
||||
isSameDay = @props.day == @props.selectedDate.getDate()
|
||||
isSameYear && isSameMonth && isSameDay
|
||||
|
||||
render: ->
|
||||
className = " #{css.day}"
|
||||
className += " active" if @_isSelected()
|
||||
dayStyle = @_dayStyle() if @props.day == 1
|
||||
|
||||
<div className={className} style={dayStyle}>
|
||||
<span onClick={@props.onClick}>{ @props.day }</span>
|
||||
</div>
|
|
@ -0,0 +1,91 @@
|
|||
CTG = React.addons.CSSTransitionGroup
|
||||
css = require './style'
|
||||
dateUtils = require '../date_utils'
|
||||
FontIcon = require '../font_icon'
|
||||
Month = require './month'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'Calendar',
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
display : React.PropTypes.oneOf(['months', 'years'])
|
||||
onChange : React.PropTypes.func
|
||||
selectedDate : React.PropTypes.object
|
||||
viewDate : React.PropTypes.object
|
||||
|
||||
getDefaultProps: ->
|
||||
display : 'months'
|
||||
selectedDate : new Date()
|
||||
|
||||
getInitialState: ->
|
||||
selectedDate : @props.selectedDate
|
||||
viewDate : @props.selectedDate
|
||||
|
||||
# -- Lifecycle
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
@_scrollToActive() if @refs.activeYear
|
||||
if prevState.selectedDate.getTime() != @state.selectedDate.getTime() && @props.onChange
|
||||
@props.onChange? @
|
||||
|
||||
# -- Events
|
||||
onDayClick: (event) ->
|
||||
@setState
|
||||
selectedDate: dateUtils.setDay(@state.viewDate, parseInt(event.target.textContent))
|
||||
|
||||
onYearClick: (event) ->
|
||||
newDate = dateUtils.setYear(@state.viewDate, parseInt(event.target.textContent))
|
||||
@setState
|
||||
selectedDate: newDate
|
||||
viewDate: newDate
|
||||
|
||||
# -- Private methods
|
||||
_scrollToActive: ->
|
||||
@refs.years.getDOMNode().scrollTop =
|
||||
@refs.activeYear.getDOMNode().offsetTop -
|
||||
@refs.years.getDOMNode().offsetHeight/2 +
|
||||
@refs.activeYear.getDOMNode().offsetHeight/2
|
||||
|
||||
# -- Public methods
|
||||
getValue: ->
|
||||
@state.selectedDate
|
||||
|
||||
incrementViewMonth: ->
|
||||
@setState
|
||||
direction: 'right'
|
||||
viewDate: dateUtils.addMonths(@state.viewDate, 1)
|
||||
|
||||
decrementViewMonth: ->
|
||||
@setState
|
||||
direction: 'left'
|
||||
viewDate: dateUtils.addMonths(@state.viewDate, -1)
|
||||
|
||||
# -- Render
|
||||
renderYear: (year) ->
|
||||
props =
|
||||
className : if year == @state.viewDate.getFullYear() then 'active' else ''
|
||||
key : "year-#{year}"
|
||||
onClick : @onYearClick
|
||||
props.ref = 'activeYear' if year == @state.viewDate.getFullYear()
|
||||
return <li {...props}>{ year }</li>
|
||||
|
||||
render: ->
|
||||
<div className={css.root}>
|
||||
{ if @props.display == 'months'
|
||||
<div className={@state.direction}>
|
||||
<FontIcon className={css.prev} value='chevron_left' onClick={@decrementViewMonth} />
|
||||
<FontIcon className={css.next} value='chevron_right' onClick={@incrementViewMonth} />
|
||||
<CTG transitionName='slide-horizontal'>
|
||||
<Month
|
||||
key={@state.viewDate.getMonth()}
|
||||
viewDate={@state.viewDate}
|
||||
selectedDate={@state.selectedDate}
|
||||
onDayClick={@onDayClick} />
|
||||
</CTG>
|
||||
</div>
|
||||
else if @props.display == 'years'
|
||||
<ul ref="years" className={css.years}>
|
||||
{ @renderYear(i) for i in [1900..2100] }
|
||||
</ul>
|
||||
}
|
||||
</div>
|
|
@ -0,0 +1,29 @@
|
|||
css = require './style'
|
||||
Day = require './day'
|
||||
util = require '../date_utils'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'Month',
|
||||
|
||||
propTypes:
|
||||
onDayClick : React.PropTypes.func
|
||||
selectedDate : React.PropTypes.object
|
||||
viewDate : React.PropTypes.object
|
||||
|
||||
render: ->
|
||||
<div>
|
||||
<span className={css.title}>
|
||||
{ util.monthInWords(@props.viewDate)} {@props.viewDate.getFullYear() }
|
||||
</span>
|
||||
<div className={css.week}>
|
||||
{ <span key={"dw#{i}"}>{ util.weekDayInWords(i).charAt(0) }</span> for i in [0..6] }
|
||||
</div>
|
||||
<div className={css.days}>
|
||||
{ for i in [1..util.daysInMonth(@props.viewDate)]
|
||||
<Day key={"d#{i}"}
|
||||
day={i}
|
||||
onClick={@props.onDayClick}
|
||||
selectedDate={@props.selectedDate}
|
||||
viewDate={@props.viewDate} /> }
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,118 @@
|
|||
@import '../constants'
|
||||
|
||||
// -- Calendar sizes
|
||||
ROW_HEIGHT = 40px
|
||||
DAY_PADDING = 2px
|
||||
TOTAL_HEIGHT = ROW_HEIGHT * 8 + DAY_PADDING * 12
|
||||
|
||||
:local(.root)
|
||||
background : WHITE
|
||||
height : TOTAL_HEIGHT
|
||||
font-size : 14px
|
||||
overflow : hidden
|
||||
position : relative
|
||||
line-height : ROW_HEIGHT
|
||||
text-align : center
|
||||
|
||||
:local(.prev), :local(.next)
|
||||
color : lighten(TEXT, 15%)
|
||||
cursor : pointer
|
||||
font-size : 26px
|
||||
height : ROW_HEIGHT
|
||||
line-height : ROW_HEIGHT
|
||||
opacity : .7
|
||||
position : absolute
|
||||
text-align : center
|
||||
top : 0
|
||||
width : ROW_HEIGHT
|
||||
z-index : Z_INDEX_HIGH
|
||||
|
||||
:local(.title)
|
||||
font-weight : 500
|
||||
|
||||
:local(.prev)
|
||||
left : 0
|
||||
|
||||
:local(.next)
|
||||
right : 0
|
||||
|
||||
:local(.week)
|
||||
display : flex
|
||||
flex-wrap : wrap
|
||||
font-size : 13px
|
||||
height : ROW_HEIGHT
|
||||
line-height : ROW_HEIGHT
|
||||
opacity : .5
|
||||
|
||||
> span
|
||||
flex : 0 0 (100/7)%
|
||||
|
||||
:local(.days)
|
||||
display : flex
|
||||
flex-wrap : wrap
|
||||
font-size : 13px
|
||||
|
||||
:local(.day)
|
||||
flex : 0 0 (100/7)%
|
||||
padding : DAY_PADDING
|
||||
|
||||
> span
|
||||
border-radius : 50%
|
||||
cursor : pointer
|
||||
display : inline-block
|
||||
height : ROW_HEIGHT
|
||||
line-height : ROW_HEIGHT
|
||||
width : ROW_HEIGHT
|
||||
|
||||
&:hover:not(.active) > span
|
||||
background : lighten(ACCENT, 65%)
|
||||
color : WHITE
|
||||
|
||||
&.active > span
|
||||
background : ACCENT
|
||||
color : WHITE
|
||||
|
||||
// -- Transitions
|
||||
:local(.root) .slide-horizontal-enter, :local(.root) .slide-horizontal-leave
|
||||
transition : all .5s
|
||||
|
||||
:local(.root) .right
|
||||
.slide-horizontal-enter
|
||||
position : absolute
|
||||
transform : translateX(100%)
|
||||
opacity : 0
|
||||
|
||||
.slide-horizontal-leave, .slide-horizontal-enter-active
|
||||
transform : translateX(0)
|
||||
opacity : 1
|
||||
|
||||
.slide-horizontal-leave-active
|
||||
transform : translateX(-100%)
|
||||
opacity : 0
|
||||
|
||||
:local(.root) .left
|
||||
.slide-horizontal-enter
|
||||
position : absolute
|
||||
transform : translateX(-100%)
|
||||
opacity : 0
|
||||
|
||||
.slide-horizontal-leave, .slide-horizontal-enter-active
|
||||
transform : translateX(0)
|
||||
opacity : 1
|
||||
|
||||
.slide-horizontal-leave-active
|
||||
transform : translateX(100%)
|
||||
opacity : 0
|
||||
|
||||
:local(.years)
|
||||
font-size : 18px
|
||||
height : 100%
|
||||
overflow : scroll
|
||||
|
||||
> li
|
||||
cursor : pointer
|
||||
line-height : 2.4
|
||||
|
||||
&.active
|
||||
color : ACCENT
|
||||
font-size : 24px
|
|
@ -0,0 +1,35 @@
|
|||
css = require './style'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Face'
|
||||
|
||||
# -- States & Properties
|
||||
getDefaultProps: ->
|
||||
active : null
|
||||
numbers : []
|
||||
radius : 0
|
||||
|
||||
# -- Internal methods
|
||||
_numberStyle: (radius, num) ->
|
||||
position : 'absolute'
|
||||
left : (radius + radius * Math.sin(360 * (Math.PI/180) / 12 * (num - 1)) + @props.spacing)
|
||||
top : (radius - radius * Math.cos(360 * (Math.PI/180) / 12 * (num - 1)) + @props.spacing)
|
||||
|
||||
_faceStyle: ->
|
||||
height : @props.radius * 2
|
||||
width : @props.radius * 2
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
<div ref="root"
|
||||
className={css.face}
|
||||
onTouchStart={@props.onTouchStart}
|
||||
onMouseDown={@props.onMouseDown}
|
||||
style={@_faceStyle()}>
|
||||
{ for i, k in @props.numbers
|
||||
<span className={css.number + (if parseInt(i) == @props.active then ' active' else '')}
|
||||
style={@_numberStyle(@props.radius - @props.spacing, k + 1)}
|
||||
key={i} >
|
||||
{i}
|
||||
</span> }
|
||||
</div>
|
|
@ -0,0 +1,125 @@
|
|||
css = require './style'
|
||||
prefixer = require "../prefixer"
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Hand'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
className : React.PropTypes.string
|
||||
initialAngle : React.PropTypes.number
|
||||
onHandChange : React.PropTypes.func
|
||||
onHandMoved : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
className : ''
|
||||
initialAngle : 0
|
||||
length : 0
|
||||
origin : {}
|
||||
|
||||
getInitialState: ->
|
||||
angle : @props.initialAngle
|
||||
knobWidth : 0
|
||||
radius : 0
|
||||
|
||||
# -- Lifecycle
|
||||
componentDidMount: ->
|
||||
@setState knobWidth: @refs.knob.getDOMNode().offsetWidth
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
if nextState.angle != @state.angle ||
|
||||
nextProps.length != @props.length &&
|
||||
@props.length != 0
|
||||
@props.onHandChange(nextState.angle)
|
||||
|
||||
# -- Event handlers
|
||||
_getMouseEventMap: ->
|
||||
mousemove : @onMouseMove
|
||||
mouseup : @onMouseUp
|
||||
|
||||
_getTouchEventMap: ->
|
||||
touchmove: @onTouchMove
|
||||
touchend: @onTouchEnd
|
||||
|
||||
onMouseMove: (event) ->
|
||||
@_move(_getMousePosition(event))
|
||||
|
||||
onTouchMove: (event) ->
|
||||
@_move(_getTouchPosition(event))
|
||||
|
||||
onMouseUp: ->
|
||||
@_end(@_getMouseEventMap())
|
||||
|
||||
onTouchEnd: ->
|
||||
@_end(@_getTouchEventMap())
|
||||
|
||||
# -- Public API
|
||||
mouseStart: (event) ->
|
||||
_addEventsToDocument(@_getMouseEventMap())
|
||||
@_move(_getMousePosition(event))
|
||||
|
||||
touchStart: (event) ->
|
||||
_addEventsToDocument(@_getTouchEventMap())
|
||||
@_move(_getTouchPosition(event))
|
||||
_pauseEvent(event)
|
||||
|
||||
# -- Internal methods
|
||||
_move: (position) ->
|
||||
@props.onHandMouseMove(@_getPositionRadius(position)) if @props.onHandMouseMove
|
||||
newDegrees = @_trimAngleToValue(@_positionToAngle(position))
|
||||
newDegrees = if newDegrees == 360 then 0 else newDegrees
|
||||
@setState(angle: newDegrees) if @state.angle != newDegrees
|
||||
|
||||
_getPositionRadius: (position) ->
|
||||
x = @props.origin.x - position.x
|
||||
y = @props.origin.y - position.y
|
||||
Math.sqrt(x * x + y * y)
|
||||
|
||||
_trimAngleToValue: (angle) ->
|
||||
@props.step * Math.round(angle/@props.step)
|
||||
|
||||
_positionToAngle: (position) ->
|
||||
_angle360(@props.origin.x, @props.origin.y, position.x, position.y)
|
||||
|
||||
_end: (events) ->
|
||||
@props.onHandMoved() if @props.onHandMoved
|
||||
_removeEventsFromDocument(events)
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
style = prefixer.transform("rotate(#{@state.angle}deg)")
|
||||
style.height = @props.length - @state.knobWidth/2
|
||||
|
||||
<div className={css.hand + ' ' + @props.className} style={style}>
|
||||
<div ref='knob' className={css.knob}></div>
|
||||
</div>
|
||||
|
||||
# -- Static Helper functions
|
||||
_addEventsToDocument = (events) ->
|
||||
document.addEventListener(key, events[key], false) for key of events
|
||||
|
||||
_removeEventsFromDocument = (events) ->
|
||||
document.removeEventListener(key, events[key], false) for key of events
|
||||
|
||||
_pauseEvent = (event) ->
|
||||
event.stopPropagation()
|
||||
event.preventDefault()
|
||||
event.returnValue = false
|
||||
event.cancelBubble = true
|
||||
return null
|
||||
|
||||
_getMousePosition = (event) ->
|
||||
x: event.pageX
|
||||
y: event.pageY
|
||||
|
||||
_getTouchPosition = (event) ->
|
||||
x: event.touches[0]['pageX']
|
||||
y: event.touches[0]['pageY']
|
||||
|
||||
_angle360 = (cx, cy, ex, ey) ->
|
||||
theta = _angle(cx, cy, ex, ey)
|
||||
if (theta < 0) then 360 + theta else theta
|
||||
|
||||
_angle = (cx, cy, ex, ey) ->
|
||||
theta = Math.atan2(ey - cy, ex - cx) + Math.PI/2
|
||||
theta * 180 / Math.PI
|
|
@ -0,0 +1,77 @@
|
|||
Face = require './face'
|
||||
Hand = require './hand'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Hours'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
format : React.PropTypes.oneOf(['24hr', 'ampm'])
|
||||
onChange : React.PropTypes.func
|
||||
onHandMoved : React.PropTypes.func
|
||||
selected : React.PropTypes.number
|
||||
|
||||
getInitialState: ->
|
||||
innerNumber : @props.format == '24hr' && 0 < @props.selected <= 12
|
||||
|
||||
# -- Events
|
||||
_onHandMouseMove: (radius) ->
|
||||
if @props.format == '24hr'
|
||||
currentInner = radius < @props.radius - @props.spacing * 2
|
||||
@setState innerNumber: currentInner if @state.innerNumber != currentInner
|
||||
|
||||
_onHandChange: (degrees) ->
|
||||
@props.onChange(@_valueFromDegrees(degrees))
|
||||
|
||||
_onMouseDown: (event)->
|
||||
@refs.hand.mouseStart(event)
|
||||
|
||||
_onTouchStart: (event) ->
|
||||
@refs.hand.touchStart(event)
|
||||
|
||||
# -- Internal Methods
|
||||
_valueFromDegrees: (degrees) ->
|
||||
if @props.format == 'ampm' || @props.format == '24hr' && @state.innerNumber
|
||||
parseInt(INNER_NUMBERS[degrees/STEP])
|
||||
else
|
||||
parseInt(OUTER_NUMBERS[degrees/STEP])
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
innerRadius = @props.radius - @props.spacing * 2
|
||||
handRadius = if @state.innerNumber then innerRadius else @props.radius
|
||||
handLength = handRadius - @props.spacing
|
||||
ampmActive = if @props.format == '24hr' then @props.selected else @props.selected % 12 || 12
|
||||
|
||||
<div>
|
||||
<Face
|
||||
onTouchStart={@_onTouchStart}
|
||||
onMouseDown={@_onMouseDown}
|
||||
numbers={if @props.format == '24hr' then OUTER_NUMBERS else INNER_NUMBERS}
|
||||
spacing={@props.spacing}
|
||||
radius={@props.radius}
|
||||
active={ampmActive} />
|
||||
{
|
||||
if @props.format == '24hr'
|
||||
<Face
|
||||
onMouseDown={@_onMouseDown}
|
||||
numbers={INNER_NUMBERS}
|
||||
spacing={@props.spacing}
|
||||
radius={innerRadius}
|
||||
active={@props.selected} />
|
||||
}
|
||||
<Hand ref='hand'
|
||||
degrees={@state.degrees}
|
||||
initialAngle={@props.selected * STEP}
|
||||
length={handLength}
|
||||
onHandMouseMove={@_onHandMouseMove}
|
||||
onHandMoved={@props.onHandMoved}
|
||||
onHandChange={@_onHandChange}
|
||||
origin={@props.center}
|
||||
step={STEP} />
|
||||
</div>
|
||||
|
||||
# -- Private constants
|
||||
INNER_NUMBERS = [12].concat([1..11])
|
||||
OUTER_NUMBERS = ['00'].concat([13..23])
|
||||
STEP = 360/12
|
|
@ -0,0 +1,100 @@
|
|||
css = require './style'
|
||||
dateUtils = require '../date_utils'
|
||||
Hours = require './hours'
|
||||
Minutes = require './minutes'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Clock'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
className : React.PropTypes.string
|
||||
display : React.PropTypes.oneOf(['hours', 'minutes'])
|
||||
format : React.PropTypes.oneOf(['24hr', 'ampm'])
|
||||
initialTime : React.PropTypes.object
|
||||
onChange : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
className : ''
|
||||
display : 'hours'
|
||||
format : '24hr'
|
||||
initialTime : new Date()
|
||||
|
||||
getInitialState: ->
|
||||
radius : 0
|
||||
time : @props.initialTime
|
||||
|
||||
# -- Lifecycle
|
||||
componentDidMount: ->
|
||||
window.addEventListener('resize', @handleResize)
|
||||
@setState radius: @_getRadius()
|
||||
|
||||
componentWillUpdate: (props, state) ->
|
||||
center = @_getCenter()
|
||||
if state.time.getTime() != @state.time.getTime() && @props.onChange
|
||||
@props.onChange(state.time)
|
||||
if @state.center?.x != center.x && @state.center?.y != center.y
|
||||
@setState center: center
|
||||
|
||||
componentWillUnmount: ->
|
||||
window.removeEventListener('resize', @handleResize)
|
||||
|
||||
# -- Events handlers
|
||||
onHourChange: (hours) ->
|
||||
@setState time: dateUtils.setHours(@state.time, @_adaptHourToFormat(hours))
|
||||
|
||||
onMinuteChange: (minutes) ->
|
||||
@setState time: dateUtils.setMinutes(@state.time, minutes)
|
||||
|
||||
# -- Helper methods
|
||||
_getRadius: ->
|
||||
@refs.wrapper.getDOMNode().getBoundingClientRect().width/2
|
||||
|
||||
_adaptHourToFormat: (hour) ->
|
||||
if @props.format == 'ampm'
|
||||
if dateUtils.timeMode(@state.time) == 'pm'
|
||||
if hour < 12 then hour + 12 else hour
|
||||
else
|
||||
if hour == 12 then 0 else hour
|
||||
else
|
||||
hour
|
||||
|
||||
handleResize: ->
|
||||
@setState
|
||||
center: @_getCenter()
|
||||
radius: @_getRadius()
|
||||
|
||||
_getCenter: ->
|
||||
bounds = @getDOMNode().getBoundingClientRect()
|
||||
{
|
||||
x: bounds.left + (bounds.right - bounds.left)/2
|
||||
y: bounds.top + (bounds.bottom - bounds.top) /2
|
||||
}
|
||||
|
||||
# -- Public methods
|
||||
toggleTimeMode: ->
|
||||
@setState time: dateUtils.toggleTimeMode(@state.time)
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
<div className={css.root}>
|
||||
<div ref="wrapper" className={css.wrapper} style={height: @state.radius * 2} >
|
||||
{
|
||||
if @props.display == 'minutes'
|
||||
<Minutes
|
||||
center={@state.center}
|
||||
onChange={@onMinuteChange}
|
||||
radius={@state.radius}
|
||||
selected={@state.time.getMinutes()}
|
||||
spacing={@state.radius * 0.16} />
|
||||
else if @props.display == 'hours'
|
||||
<Hours
|
||||
center={@state.center}
|
||||
format={@props.format}
|
||||
onChange={@onHourChange}
|
||||
radius={@state.radius}
|
||||
selected={@state.time.getHours()}
|
||||
spacing={@state.radius * 0.16} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,49 @@
|
|||
Face = require './face'
|
||||
Hand = require './hand'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'Minutes'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
selected : React.PropTypes.number
|
||||
onChange : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
selected : 0
|
||||
onChange : null
|
||||
|
||||
# -- Events
|
||||
_onHandChange: (degrees) ->
|
||||
@props.onChange(degrees/STEP)
|
||||
|
||||
_onMouseDown: (event)->
|
||||
@refs.hand.mouseStart(event)
|
||||
|
||||
_onTouchStart: (event) ->
|
||||
@refs.hand.touchStart(event)
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
handClass = if MINUTES.indexOf(('0' + @props.selected).slice(-2)) == -1 then 'smallKnob' else ''
|
||||
|
||||
<div>
|
||||
<Face
|
||||
onTouchStart={@_onTouchStart}
|
||||
onMouseDown={@_onMouseDown}
|
||||
numbers={MINUTES}
|
||||
spacing={@props.spacing}
|
||||
radius={@props.radius}
|
||||
active={@props.selected} />
|
||||
<Hand ref='hand'
|
||||
className={handClass}
|
||||
initialAngle={@props.selected * STEP}
|
||||
length={@props.radius - @props.spacing}
|
||||
onHandChange={@_onHandChange}
|
||||
origin={@props.center}
|
||||
step={STEP} />
|
||||
</div>
|
||||
|
||||
# -- Private constants
|
||||
MINUTES = (('0' + i).slice(-2) for i in [0..55] by 5)
|
||||
STEP = 360/60
|
|
@ -0,0 +1,98 @@
|
|||
@import '../constants'
|
||||
|
||||
NUMBER_SIZE = 20px
|
||||
HAND_WIDTH = 4px
|
||||
HAND_DOT_SIZE = 10px
|
||||
KNOB_SIZE = 34px
|
||||
SMALL_KNOB_SIZE = 12px
|
||||
|
||||
:local(.root)
|
||||
padding : 15px
|
||||
|
||||
:local(.wrapper)
|
||||
background-color : DIVIDER
|
||||
border-radius : 50%
|
||||
position : relative
|
||||
|
||||
:local(.face)
|
||||
border-radius : 50%
|
||||
cursor : pointer
|
||||
position : relative
|
||||
z-index : Z_INDEX_HIGH
|
||||
|
||||
:local(.number)
|
||||
height : NUMBER_SIZE
|
||||
margin-left : -(NUMBER_SIZE/2)
|
||||
margin-top : -(NUMBER_SIZE/2)
|
||||
pointer-events : none
|
||||
position : relative
|
||||
text-align : center
|
||||
user-select : none
|
||||
width : NUMBER_SIZE
|
||||
|
||||
&.active
|
||||
color : WHITE
|
||||
|
||||
:local(.face)
|
||||
position : absolute
|
||||
top : 50%
|
||||
left : 50%
|
||||
transform : translateX(-50%) translateY(-50%)
|
||||
|
||||
:local(.hand)
|
||||
background-color : ACCENT
|
||||
bottom : 50%
|
||||
display : block
|
||||
left : 50%
|
||||
margin-left : -(HAND_WIDTH/2)
|
||||
position : absolute
|
||||
transform-origin : 50% 100%
|
||||
width : HAND_WIDTH
|
||||
|
||||
&:before
|
||||
background-color : ACCENT
|
||||
border-radius : 50%
|
||||
bottom : 0
|
||||
content : ''
|
||||
height : HAND_DOT_SIZE
|
||||
left : 50%
|
||||
margin-bottom : -(HAND_DOT_SIZE/2)
|
||||
margin-left : -(HAND_DOT_SIZE/2)
|
||||
position : absolute
|
||||
width : HAND_DOT_SIZE
|
||||
|
||||
&.smallKnob :local(.knob)
|
||||
background-color: lighten(ACCENT, 70%)
|
||||
|
||||
&:after
|
||||
background : ACCENT
|
||||
border-radius : 50%
|
||||
content : ''
|
||||
height : SMALL_KNOB_SIZE
|
||||
left : 50%
|
||||
margin-left : -(SMALL_KNOB_SIZE/2)
|
||||
margin-top : -(SMALL_KNOB_SIZE/2)
|
||||
position : absolute
|
||||
top : 50%
|
||||
width : SMALL_KNOB_SIZE
|
||||
|
||||
&:before
|
||||
background : ACCENT
|
||||
bottom : 0
|
||||
content : ''
|
||||
height : KNOB_SIZE - SMALL_KNOB_SIZE
|
||||
left : 50%
|
||||
margin-left : -(HAND_WIDTH/2)
|
||||
position : absolute
|
||||
width : HAND_WIDTH
|
||||
|
||||
:local(.knob)
|
||||
background-color : ACCENT
|
||||
border-radius : 50%
|
||||
cursor : pointer
|
||||
height : KNOB_SIZE
|
||||
left : 50%
|
||||
margin-left : -(KNOB_SIZE/2)
|
||||
position : absolute
|
||||
top : -(KNOB_SIZE)
|
||||
width : KNOB_SIZE
|
|
@ -1,4 +1,4 @@
|
|||
@import url('http://fonts.googleapis.com/css?family=Roboto:300,400,700')
|
||||
@import url('http://fonts.googleapis.com/css?family=Roboto:300,400,500,700')
|
||||
@import './vendor.styl'
|
||||
@import './normalize.styl'
|
||||
|
||||
|
|
|
@ -50,3 +50,10 @@ ZDEPTH_SHADOW_5 = 0 19px 60px rgba(0,0,0,0.30), 0 15px 20px rgba(0,0
|
|||
ANIMATION_DURATION = 450ms
|
||||
ANIMATION_EASE = cubic-bezier(.55,0,.1,1)
|
||||
ANIMATION_DELAY = (ANIMATION_DURATION / 5)
|
||||
|
||||
// -- Z Indexes
|
||||
Z_INDEX_HIGHER = 200
|
||||
Z_INDEX_HIGH = 100
|
||||
Z_INDEX_NORMAL = 1
|
||||
Z_INDEX_LOW = -100
|
||||
Z_INDEX_LOWER = -200
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
# DatePicker
|
||||
|
||||
```javascript
|
||||
var DatePicker = require('react-toolbox/components/date_picker');
|
||||
|
||||
var date = new Date(1995,11,17);
|
||||
|
||||
// Initialized date picker
|
||||
<DatePicker value={date} />
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Name | Type | Default | Description|
|
||||
| ------------- |:-------:|:--------------- |:---------- |
|
||||
| **value** | Date | `new Date()` | Date object with currrent date by default |
|
||||
|
||||
## Methods
|
||||
|
||||
#### getValue
|
||||
Returns the value of the picker.
|
||||
|
||||
```
|
||||
input_instance.getValue();
|
||||
```
|
|
@ -0,0 +1,70 @@
|
|||
css = require './style'
|
||||
dateUtils = require '../date_utils'
|
||||
Dialog = require '../dialog'
|
||||
Calendar = require '../calendar'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'CalendarDialog'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
initialDate : React.PropTypes.object
|
||||
onDateSelected : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
initialDate : new Date()
|
||||
|
||||
getInitialState: ->
|
||||
date : @props.initialDate
|
||||
display : 'months'
|
||||
actions : [
|
||||
{ caption: "Cancel", type: "flat accent", onClick: @onDateCancel },
|
||||
{ caption: "Ok", type: "flat accent", onClick: @onDateSelected }
|
||||
]
|
||||
|
||||
# -- Events
|
||||
onCalendarChange: (calendar) ->
|
||||
@setState
|
||||
date: dateUtils.cloneDatetime(calendar.getValue())
|
||||
display: 'months'
|
||||
|
||||
onDateCancel: (ref, method) ->
|
||||
@refs.dialog.hide()
|
||||
|
||||
onDateSelected: ->
|
||||
@props.onDateSelected(@state.date) if @props.onDateSelected
|
||||
@refs.dialog.hide()
|
||||
|
||||
# -- Public methods
|
||||
show: ->
|
||||
@refs.dialog.show()
|
||||
|
||||
displayMonths: ->
|
||||
@setState display: 'months'
|
||||
|
||||
displayYears: ->
|
||||
@setState display: 'years'
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
className = "display-#{@state.display}"
|
||||
<Dialog ref="dialog" type={css.dialog} className={className} actions={@state.actions}>
|
||||
<header className={css.header}>
|
||||
<span className={css.headerWeekday}>{dateUtils.weekDayInWords(@state.date.getDay())}</span>
|
||||
<div onClick={@displayMonths}>
|
||||
<span className={css.headerMonth}>{dateUtils.monthInShortWords(@state.date)}</span>
|
||||
<span className={css.headerDay}>{@state.date.getDate()}</span>
|
||||
</div>
|
||||
<span className={css.headerYear} onClick={@displayYears}>
|
||||
{@state.date.getFullYear()}
|
||||
</span>
|
||||
</header>
|
||||
|
||||
<div className={css.calendarWrapper}>
|
||||
<Calendar
|
||||
ref="calendar"
|
||||
display={@state.display}
|
||||
onChange={@onCalendarChange}
|
||||
selectedDate={@state.date} />
|
||||
</div>
|
||||
</Dialog>
|
|
@ -0,0 +1,52 @@
|
|||
css = require './style'
|
||||
dateUtils = require '../date_utils'
|
||||
Input = require '../input'
|
||||
CalendarDialog = require './dialog'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'DatePicker'
|
||||
|
||||
propTypes:
|
||||
className : React.PropTypes.string
|
||||
value : React.PropTypes.object
|
||||
|
||||
getDefaultProps: ->
|
||||
className : ''
|
||||
|
||||
getInitialState: ->
|
||||
value : @props.value
|
||||
|
||||
# -- Events
|
||||
openCalendarDialog: ->
|
||||
@refs.dialog.show()
|
||||
|
||||
onDateSelected: (value) ->
|
||||
@refs.input.setValue(@formatDate(value))
|
||||
@setState value: value
|
||||
|
||||
# -- Private methods
|
||||
formatDate: (date) ->
|
||||
day = date.getDate()
|
||||
month = dateUtils.monthInWords(date)
|
||||
year = date.getFullYear()
|
||||
"#{day} #{month} #{year}"
|
||||
|
||||
# -- Public methods
|
||||
getValue: ->
|
||||
@state.value
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
<div>
|
||||
<Input
|
||||
ref="input"
|
||||
type="text"
|
||||
disabled={true}
|
||||
onClick={@openCalendarDialog}
|
||||
placeholder="Pick up date"
|
||||
value={@formatDate(@state.value) if @state.value} />
|
||||
<CalendarDialog
|
||||
ref="dialog"
|
||||
initialDate={@state.value}
|
||||
onDateSelected={@onDateSelected} />
|
||||
</div>
|
|
@ -0,0 +1,53 @@
|
|||
@import '../constants'
|
||||
|
||||
TOTAL_WIDTH = 330px
|
||||
|
||||
:local(.dialog)
|
||||
padding : 0
|
||||
text-align : center
|
||||
width : TOTAL_WIDTH
|
||||
|
||||
> nav
|
||||
margin-top : 0
|
||||
padding-bottom : 10px
|
||||
|
||||
:local(.header)
|
||||
background-color : ACCENT
|
||||
color : white
|
||||
|
||||
:local(.headerWeekday)
|
||||
background-color : darken(ACCENT, 20%)
|
||||
display : block
|
||||
font-size : 14px
|
||||
line-height : 1.8
|
||||
width : 100%
|
||||
|
||||
:local(.headerMonth)
|
||||
cursor : pointer
|
||||
display : block
|
||||
font-size : 18px
|
||||
padding : 10px
|
||||
text-transform : uppercase
|
||||
|
||||
:local(.headerDay)
|
||||
cursor : pointer
|
||||
display : block
|
||||
font-size : 50px
|
||||
line-height : 40px
|
||||
|
||||
:local(.headerYear)
|
||||
cursor : pointer
|
||||
display : block
|
||||
font-size : 18px
|
||||
padding : 10px
|
||||
|
||||
:local(.calendarWrapper)
|
||||
padding : 10px 5px
|
||||
|
||||
:local(.dialog)
|
||||
&.display-years
|
||||
:local(.headerDay), :local(.headerMonth)
|
||||
opacity : .7
|
||||
|
||||
&.display-months :local(.headerYear)
|
||||
opacity : .7
|
|
@ -0,0 +1,103 @@
|
|||
module.exports =
|
||||
daysInMonth: (date) ->
|
||||
(new Date(date.getFullYear(), date.getMonth() + 1, 0)).getDate()
|
||||
|
||||
firstWeekDay: (date) ->
|
||||
(new Date(date.getFullYear(), date.getMonth(), 1)).getDay()
|
||||
|
||||
monthInWords: (date) ->
|
||||
switch (date.getMonth())
|
||||
when 0 then 'January'
|
||||
when 1 then 'February'
|
||||
when 2 then 'March'
|
||||
when 3 then 'April'
|
||||
when 4 then 'May'
|
||||
when 5 then 'June'
|
||||
when 6 then 'July'
|
||||
when 7 then 'August'
|
||||
when 8 then 'September'
|
||||
when 9 then 'October'
|
||||
when 10 then 'November'
|
||||
when 11 then 'December'
|
||||
|
||||
monthInShortWords: (date) ->
|
||||
switch (date.getMonth())
|
||||
when 0 then 'Jan'
|
||||
when 1 then 'Feb'
|
||||
when 2 then 'Mar'
|
||||
when 3 then 'Apr'
|
||||
when 4 then 'May'
|
||||
when 5 then 'Jun'
|
||||
when 6 then 'Jul'
|
||||
when 7 then 'Aug'
|
||||
when 8 then 'Sep'
|
||||
when 9 then 'Oct'
|
||||
when 10 then 'Nov'
|
||||
when 11 then 'Dec'
|
||||
|
||||
weekDayInWords: (day) ->
|
||||
switch (day)
|
||||
when 0 then 'Sunday'
|
||||
when 1 then 'Monday'
|
||||
when 2 then 'Tuesday'
|
||||
when 3 then 'Wednesday'
|
||||
when 4 then 'Thursday'
|
||||
when 5 then 'Friday'
|
||||
when 6 then 'Saturday'
|
||||
|
||||
weekDayInShortWords: (day) ->
|
||||
switch (day)
|
||||
when 0 then 'Sun'
|
||||
when 1 then 'Mon'
|
||||
when 2 then 'Tue'
|
||||
when 3 then 'Wed'
|
||||
when 4 then 'Thu'
|
||||
when 5 then 'Fri'
|
||||
when 6 then 'Sat'
|
||||
|
||||
addDays: (date, days) ->
|
||||
newDate = @cloneDatetime(date)
|
||||
newDate.setDate(date.getDate() + days)
|
||||
newDate
|
||||
|
||||
addMonths: (date, months) ->
|
||||
newDate = @cloneDatetime(date)
|
||||
newDate.setMonth(date.getMonth() + months)
|
||||
newDate
|
||||
|
||||
addYears: (date, years) ->
|
||||
newDate = @cloneDatetime(date)
|
||||
newDate.setFullYear(date.getFullYear() + years)
|
||||
newDate
|
||||
|
||||
setDay: (date, day) ->
|
||||
newDate = @cloneDatetime(date)
|
||||
newDate.setDate(day)
|
||||
newDate
|
||||
|
||||
setYear: (date, year) ->
|
||||
newDate = @cloneDatetime(date)
|
||||
newDate.setFullYear(year)
|
||||
newDate
|
||||
|
||||
cloneDatetime: (date) ->
|
||||
new Date(date.getTime())
|
||||
|
||||
timeMode: (datetime) ->
|
||||
if datetime.getHours() >= 12 then 'pm' else 'am'
|
||||
|
||||
toggleTimeMode: (datetime) ->
|
||||
newDatetime = @cloneDatetime(datetime)
|
||||
hours = datetime.getHours()
|
||||
if hours > 12 then newDatetime.setHours(hours - 12) else newDatetime.setHours(hours + 12)
|
||||
newDatetime
|
||||
|
||||
setHours: (datetime, hours) ->
|
||||
newDatetime = @cloneDatetime(datetime)
|
||||
newDatetime.setHours(hours)
|
||||
newDatetime
|
||||
|
||||
setMinutes: (datetime, minutes) ->
|
||||
newDatetime = @cloneDatetime(datetime)
|
||||
newDatetime.setMinutes(minutes)
|
||||
newDatetime
|
|
@ -3,6 +3,7 @@ Button = require '../button'
|
|||
Navigation = require '../navigation'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Dialog'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
|
@ -21,12 +22,14 @@ module.exports = React.createClass
|
|||
|
||||
# -- Render
|
||||
render: ->
|
||||
className = "#{localCSS.root} #{@props.className}"
|
||||
className += " #{@props.type}" if @props.type
|
||||
className += ' active' if @state.active
|
||||
<div data-react-toolbox='dialog' data-flex='vertical center' className={className}>
|
||||
<div className={localCSS.container}>
|
||||
{<h1>{@props.title}</h1>}
|
||||
rootClass = localCSS.root
|
||||
rootClass += ' active' if @state.active
|
||||
containerClass = "#{localCSS.container} #{@props.className}"
|
||||
containerClass += " #{@props.type}" if @props.type
|
||||
|
||||
<div data-react-toolbox='dialog' data-flex='vertical center' className={rootClass}>
|
||||
<div className={containerClass}>
|
||||
{<h1>{@props.title}</h1> if @props.title}
|
||||
{@props.children}
|
||||
{<Navigation actions={@props.actions}/> if @props.actions.length > 0}
|
||||
</div>
|
||||
|
|
|
@ -29,13 +29,7 @@
|
|||
transition-property : opacity
|
||||
transition-duration : (ANIMATION_DURATION / 2)
|
||||
transition-timing-function : ANIMATION_EASE
|
||||
// -- Overrides
|
||||
&.small > :local(.container)
|
||||
width : 33vw
|
||||
&.normal > :local(.container)
|
||||
width : 50vw
|
||||
&.large > :local(.container)
|
||||
width : 96vw
|
||||
|
||||
&:not(.active)
|
||||
pointer-events : none
|
||||
opacity : 0
|
||||
|
@ -48,3 +42,11 @@
|
|||
> :local(.container)
|
||||
opacity : 1
|
||||
transform : translateY(0%)
|
||||
|
||||
// -- Overrides
|
||||
:local(.container).small
|
||||
width : 33vw
|
||||
:local(.container).normal
|
||||
width : 50vw
|
||||
:local(.container).large
|
||||
width : 96vw
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
localCSS = require './style'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'FontIcon',
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
|
@ -10,7 +11,10 @@ module.exports = React.createClass
|
|||
getDefaultProps: ->
|
||||
className : ''
|
||||
|
||||
onClick: (event) ->
|
||||
@props.onClick? @props.onClick(event)
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
className = "#{localCSS.root} #{@props.className} #{@props.value}"
|
||||
<span data-react-toolbox='icon' className={className} />
|
||||
<span data-react-toolbox='icon' className={className} onClick={@onClick} />
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
localCSS = require './style'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Input'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
|
|
|
@ -3,6 +3,7 @@ Button = require '../button'
|
|||
Link = require '../link'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'Navigation'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
css = require './style'
|
||||
dateUtils = require '../date_utils'
|
||||
|
||||
Button = require '../button'
|
||||
Clock = require '../clock'
|
||||
Dialog = require '../dialog'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'TimePickerDialog'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
className : React.PropTypes.string
|
||||
initialTime : React.PropTypes.object
|
||||
format : React.PropTypes.oneOf(['24hr', 'ampm'])
|
||||
onTimeSelected : React.PropTypes.func
|
||||
|
||||
getDefaultProps: ->
|
||||
className : ''
|
||||
initialTime : new Date()
|
||||
format : '24hr'
|
||||
|
||||
getInitialState: ->
|
||||
display : 'hours'
|
||||
time : @props.initialTime
|
||||
actions: [
|
||||
{ caption: "Cancel", type: "flat accent", onClick: @onTimeCancel },
|
||||
{ caption: "Ok", type: "flat accent", onClick: @onTimeSelected }
|
||||
]
|
||||
|
||||
# -- Events
|
||||
onClockChange: (time) ->
|
||||
@setState time: time
|
||||
|
||||
onTimeCancel: (ref, method) ->
|
||||
@refs.dialog.hide()
|
||||
|
||||
onTimeSelected: ->
|
||||
@props.onTimeSelected(@state.time) if @props.onTimeSelected
|
||||
@refs.dialog.hide()
|
||||
|
||||
# -- Public methods
|
||||
displayMinutes: ->
|
||||
@setState display: 'minutes'
|
||||
|
||||
displayHours: ->
|
||||
@setState display: 'hours'
|
||||
|
||||
toggleTimeMode: ->
|
||||
@refs.clock.toggleTimeMode()
|
||||
|
||||
show: ->
|
||||
@refs.dialog.show()
|
||||
setTimeout @refs.clock.handleResize, 500
|
||||
|
||||
# -- Private helpers
|
||||
_formatHours: ->
|
||||
if @props.format == 'ampm' then @state.time.getHours() % 12 || 12 else @state.time.getHours()
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
className = " "
|
||||
className += " display-#{@state.display}"
|
||||
className += " format-#{dateUtils.timeMode(@state.time)}"
|
||||
|
||||
<Dialog ref="dialog" type={css.dialog} className={className} actions={@state.actions}>
|
||||
<header className={css.header}>
|
||||
<span className={css.hours} onClick={@displayHours} >
|
||||
{_twoDigits(@_formatHours())}
|
||||
</span>
|
||||
<span className={css.separator}>:</span>
|
||||
<span className={css.minutes} onClick={@displayMinutes}>
|
||||
{_twoDigits(@state.time.getMinutes())}
|
||||
</span>
|
||||
{
|
||||
if @props.format == 'ampm'
|
||||
<div className={css.ampm}>
|
||||
<span className={css.am} onClick={@toggleTimeMode}>AM</span>
|
||||
<span className={css.pm} onClick={@toggleTimeMode}>PM</span>
|
||||
</div>
|
||||
}
|
||||
</header>
|
||||
<Clock ref="clock"
|
||||
display={@state.display}
|
||||
format={@props.format}
|
||||
initialTime={@props.initialTime}
|
||||
onChange={@onClockChange} />
|
||||
</Dialog>
|
||||
|
||||
# -- Private helpers
|
||||
_twoDigits = (number) ->
|
||||
('0' + number).slice(-2)
|
|
@ -0,0 +1,64 @@
|
|||
css = require './style'
|
||||
Input = require '../input'
|
||||
TimeDialog = require './dialog'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName : 'TimePicker'
|
||||
|
||||
# -- States & Properties
|
||||
propTypes:
|
||||
format : React.PropTypes.oneOf(['24hr', 'ampm'])
|
||||
value : React.PropTypes.object
|
||||
|
||||
getDefaultProps: ->
|
||||
format : '24hr'
|
||||
|
||||
getInitialState: ->
|
||||
value : @props.value
|
||||
|
||||
# -- Events
|
||||
onTimeSelected: (time) ->
|
||||
@refs.input.setValue(@formatTime(time))
|
||||
@setState value: time
|
||||
|
||||
openTimeDialog: ->
|
||||
@refs.dialog.show()
|
||||
|
||||
# -- Private methods
|
||||
formatTime: (date) ->
|
||||
hours = date.getHours()
|
||||
mins = date.getMinutes().toString()
|
||||
|
||||
if (@props.format == "ampm")
|
||||
isAM = hours < 12
|
||||
hours = hours % 12
|
||||
additional = if isAM then " am" else " pm"
|
||||
hours = (hours || 12).toString()
|
||||
mins = "0" + mins if (mins.length < 2 )
|
||||
return hours + (if mins == "00" then "" else ":" + mins) + additional
|
||||
|
||||
hours = hours.toString()
|
||||
hours = "0" + hours if (hours.length < 2)
|
||||
mins = "0" + mins if (mins.length < 2)
|
||||
return hours + ":" + mins
|
||||
|
||||
# -- Public methods
|
||||
getValue: ->
|
||||
@state.value
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
<div>
|
||||
<Input
|
||||
ref="input"
|
||||
type="text"
|
||||
disabled={true}
|
||||
onClick={@openTimeDialog}
|
||||
placeholder="Pick up time"
|
||||
value={@formatTime(@state.value) if @state.value} />
|
||||
<TimeDialog
|
||||
ref="dialog"
|
||||
initialTime={@state.value}
|
||||
format={@props.format}
|
||||
onTimeSelected={@onTimeSelected} />
|
||||
</div>
|
|
@ -0,0 +1,60 @@
|
|||
@import '../constants'
|
||||
|
||||
AMPM_HEIGHT = 22px
|
||||
AMPM_WIDTH = 40px
|
||||
PICKER_WIDTH = 330px
|
||||
|
||||
// Picker dialog
|
||||
:local(.dialog)
|
||||
padding : 0
|
||||
width : PICKER_WIDTH
|
||||
> nav
|
||||
margin-top : 0
|
||||
padding-bottom : 10px
|
||||
|
||||
:local(.header)
|
||||
background : ACCENT
|
||||
color : WHITE
|
||||
font-size : 52px
|
||||
padding : 10px
|
||||
text-align : center
|
||||
position : relative
|
||||
width : 100%
|
||||
|
||||
:local(.hours), :local(.minutes)
|
||||
cursor : pointer
|
||||
display : inline-block
|
||||
opacity : .6
|
||||
|
||||
:local(.separator)
|
||||
margin : 0 5px
|
||||
opacity : .6
|
||||
|
||||
:local(.ampm)
|
||||
font-size : 16px
|
||||
height : AMPM_HEIGHT * 2
|
||||
line-height : AMPM_HEIGHT
|
||||
margin-top : - AMPM_HEIGHT
|
||||
position : absolute
|
||||
right : 20px
|
||||
text-align : center
|
||||
top : 50%
|
||||
width : AMPM_WIDTH
|
||||
|
||||
:local(.am), :local(.pm)
|
||||
cursor : pointer
|
||||
display : block
|
||||
opacity : .6
|
||||
|
||||
// Modifiers
|
||||
:local(.dialog).display-hours :local(.hours)
|
||||
opacity : 1
|
||||
|
||||
:local(.dialog).display-minutes :local(.minutes)
|
||||
opacity : 1
|
||||
|
||||
:local(.dialog).format-am :local(.am)
|
||||
opacity : 1
|
||||
|
||||
:local(.dialog).format-pm :local(.pm)
|
||||
opacity : 1
|
|
@ -0,0 +1,28 @@
|
|||
# TimePicker
|
||||
|
||||
```javascript
|
||||
var TimePicker = require('react-toolbox/components/time_picker');
|
||||
|
||||
var time = new Date();
|
||||
time.setHours(17);
|
||||
time.setMinutes(28);
|
||||
|
||||
// Initialized time picker with AM-PM format
|
||||
<TimePicker format="ampm" value={time} />
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Name | Type | Default | Description|
|
||||
| ------------- |:-------:|:--------------- |:---------- |
|
||||
| **format** | String | `24hr` | Format to display the clock. It can be *24hr* or *ampm*.|
|
||||
| **value** | Date | `new Date()` | Datetime object with currrent time by default |
|
||||
|
||||
## Methods
|
||||
|
||||
#### getValue
|
||||
Returns the value of the picker.
|
||||
|
||||
```
|
||||
input_instance.getValue();
|
||||
```
|
|
@ -0,0 +1,7 @@
|
|||
Calendar = require '../../components/calendar'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'TestCalendar',
|
||||
|
||||
render: ->
|
||||
<Calendar />
|
|
@ -0,0 +1,7 @@
|
|||
Clock = require '../../components/clock'
|
||||
|
||||
module.exports = React.createClass
|
||||
render: ->
|
||||
<div>
|
||||
<Clock display="hours" />
|
||||
</div>
|
|
@ -0,0 +1,19 @@
|
|||
DatePicker = require '../../components/date_picker'
|
||||
TimePicker = require '../../components/time_picker'
|
||||
|
||||
module.exports = React.createClass
|
||||
displayName: 'PickersTest'
|
||||
|
||||
render: ->
|
||||
datetime = new Date(1995,11,17)
|
||||
datetime.setHours(17)
|
||||
datetime.setMinutes(28)
|
||||
|
||||
<section>
|
||||
<h2>Pickers</h2>
|
||||
|
||||
<DatePicker />
|
||||
<DatePicker value={datetime} />
|
||||
<TimePicker value={datetime} />
|
||||
<TimePicker format="ampm" />
|
||||
</section>
|
|
@ -5,6 +5,7 @@ Aside = require './components/aside'
|
|||
Autocomplete = require './components/autocomplete'
|
||||
Button = require './components/button'
|
||||
Card = require './components/card'
|
||||
Calendar = require '../components/calendar'
|
||||
Dialog = require './components/dialog'
|
||||
Dropdown = require './components/dropdown'
|
||||
FontIcon = require './components/font_icon'
|
||||
|
@ -12,9 +13,13 @@ Form = require './components/form'
|
|||
Progress = require './components/progress'
|
||||
Slider = require './components/slider'
|
||||
Switch = require './components/switch'
|
||||
Calendar = require './components/calendar'
|
||||
Pickers = require './components/pickers'
|
||||
Clock = require './components/clock'
|
||||
Tabs = require './components/tabs'
|
||||
|
||||
Test = React.createClass
|
||||
displayName: 'App'
|
||||
|
||||
# -- Render
|
||||
render: ->
|
||||
|
@ -33,6 +38,7 @@ Test = React.createClass
|
|||
<Slider />
|
||||
<Switch />
|
||||
<Tabs />
|
||||
<Pickers />
|
||||
</app>
|
||||
|
||||
React.render <Test/>, document.body
|
||||
|
|
Loading…
Reference in New Issue