diff --git a/components/clock/index.cjsx b/components/clock/index.cjsx new file mode 100644 index 000000000..f2dce8556 --- /dev/null +++ b/components/clock/index.cjsx @@ -0,0 +1,117 @@ +css = require './style' + +module.exports = React.createClass + + getInitialState: -> + angle: 0 + pressed: false + + # -- Lifecycle + componentDidMount: -> + clockBounds = @refs.clock.getDOMNode().getBoundingClientRect() + @setState + center: @_getClockCenter() + minRadius: @_getMinRadius() + maxRadius: @_getMaxRadius() + + onKnobMouseDown: -> + @setState pressed: true + _addEventsToDocument(@getMouseEventMap()) + + getMouseEventMap: -> + mousemove: @onMouseMove + mouseup: @onMouseUp + + onMouseMove: (event) -> + radius = @_getClickRadius(event) + if (@state.minRadius < radius < @state.maxRadius) + @setState angle: @_trimValue(@_getAngleFromClickEvent(event)) + + onMouseUp: -> + @setState pressed: false + @end(@getMouseEventMap()) + + end: (events) -> + _removeEventsFromDocument(events) + + _getClockCenter: -> + bounds = @refs.clock.getDOMNode().getBoundingClientRect() + return { + x: bounds.left + (bounds.right - bounds.left)/2 + y: bounds.top + (bounds.bottom - bounds.top) /2 + } + + _getMinRadius: -> + bounds = @refs.clockInner.getDOMNode().getBoundingClientRect() + (bounds.right - bounds.left)/2 + + _getMaxRadius: -> + bounds = @refs.root.getDOMNode().getBoundingClientRect() + (bounds.right - bounds.left)/2 + + _getAngleFromClickEvent: (event) -> + mouse = _getMousePosition(event) + _angle360(@state.center.x, @state.center.y, mouse.x, mouse.y) + + onClockClick: (event) -> + radius = @_getClickRadius(event) + if (@state.minRadius < radius < @state.maxRadius) + @setState angle: @_trimValue(@_getAngleFromClickEvent(event)) + + _trimValue: (angle) -> + step = 360/12 + step * Math.round(angle/step) + + _getClickRadius: (event) -> + mouse = _getMousePosition(event) + x = @state.center.x - mouse.x + y = @state.center.y - mouse.y + r = Math.sqrt(x * x + y * y) + return r + + render: -> + className = '' + className += ' pressed' if @state.pressed + handStyle = + transform: "rotate(#{@state.angle}deg)" + +
+
+
+
+
+
+ { {i} for i in [1..12] } +
+
+
+
+ +_pauseEvent = (event) -> + event.stopPropagation() + event.preventDefault() + event.returnValue = false + event.cancelBubble = true + return null + +_getMousePosition = (event) -> + x: event.pageX + y: event.pageY + +_angle = (cx, cy, ex, ey) -> + dy = ey - cy; + dx = ex - cx; + theta = Math.atan2(dy, dx) + Math.PI/2 + theta = theta * 180 / Math.PI + return theta + +_angle360 = (cx, cy, ex, ey) -> + theta = _angle(cx, cy, ex, ey) + theta = 360 + theta if (theta < 0) + return theta + +_addEventsToDocument = (events) -> + document.addEventListener(key, events[key], false) for key of events + +_removeEventsFromDocument = (events) -> + document.removeEventListener(key, events[key], false) for key of events diff --git a/components/clock/style.styl b/components/clock/style.styl new file mode 100644 index 000000000..1bdc26d0c --- /dev/null +++ b/components/clock/style.styl @@ -0,0 +1,94 @@ +rootSize = 266px +innerPadding = 10px +knobSize = 40px +clockSize = rootSize - knobSize - innerPadding +clockSizeInner = clockSize - knobSize - innerPadding +clockRadius = (clockSize/2) +hourSize = 30px +handWidth = 2px + +:local(.root) + border-radius : 50% + border : 1px solid red + height : rootSize + margin-left : 35px + padding : (hourSize/2) + position : relative + width : rootSize + +:local(.clock) + border-radius : 50% + height : clockSize + left : 50% + margin-left : -(clockSize/2) + margin-top : -(clockSize/2) + position : absolute + top : 50% + width : clockSize + +:local(.clockInner) + border-radius : 50% + border: 1px solid blue + height : clockSizeInner + left : 50% + margin-left : -(clockSizeInner/2) + margin-top : -(clockSizeInner/2) + position : absolute + top : 50% + width : clockSizeInner + z-index : 2 + +:local(.hand) + background-color : blue + bottom : 50% + display : block + height : clockRadius - (knobSize/2) + left : 50% + margin-left : -(handWidth/2) + position : absolute + transform-origin : 0 100% + width : handWidth + + &:before + background-color : blue + border-radius : 50% + bottom : 0 + content : '' + height : 8px + left : 50% + margin-bottom : -4px + margin-left : -4px + position : absolute + width : 8px + +:local(.knob) + cursor : pointer + border-radius : 50% + background-color : blue + height : knobSize + left : 50% + margin-left : -(knobSize/2) + position : absolute + top : -(knobSize) + width : knobSize + +:local(.root).pressed :local(.knob) + background-color : pink + +:local(.hour) + color : #444 + border-radius : 50% + height : hourSize + line-height : hourSize + margin-left : -(hourSize/2) + pointer-events : none + margin-top : -(hourSize/2) + position : absolute + text-align : center + width : hourSize + +:local(.hours) + for num in 1 2 3 4 5 6 7 8 9 10 11 12 + :local(.hour):nth-child({num}) + left : (clockRadius + clockRadius * sin(PI * 2 * (num/12)))px + top : (clockRadius - clockRadius * cos(PI * 2 * (num/12)))px