diff --git a/components/constants.styl b/components/constants.styl index 410f96c6..b658f468 100644 --- a/components/constants.styl +++ b/components/constants.styl @@ -34,6 +34,7 @@ INPUT_HEIGHT = (2 * SPACE) BUTTON_HEIGHT = (2.5 * SPACE) BUTTON_CIRCLE_HEIGHT = (2.75 * SPACE) LOADING_HEIGHT = (1.5 * UNIT) +PROGRESS_BAR_HEIGHT = (SPACE / 4) // -- Shadows ZDEPTH_SHADOW_1 = 0 1px 6px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.24) diff --git a/components/index.coffee b/components/index.coffee index 2d690e24..da40a088 100644 --- a/components/index.coffee +++ b/components/index.coffee @@ -14,5 +14,6 @@ module.exports = Loading : require "./loading" Navigation : require "./navigation" Ripple : require "./ripple" + ProgressBar : require "./progress_bar" # -- Tools Router : require "./router" diff --git a/components/progress_bar/index.cjsx b/components/progress_bar/index.cjsx new file mode 100644 index 00000000..750f5e53 --- /dev/null +++ b/components/progress_bar/index.cjsx @@ -0,0 +1,69 @@ +### +@todo +v2 + - can set different sizes for circular progress + - maybe a multicolor indeterminate circular progress bar + - refactor vendor prefixes adding to a module +### + +require './style' + +module.exports = React.createClass + + # -- Properties + propTypes: + buffer : React.PropTypes.number + className : React.PropTypes.string + max : React.PropTypes.number + min : React.PropTypes.number + mode : React.PropTypes.string + type : React.PropTypes.string + value : React.PropTypes.number + + getDefaultProps: -> + buffer : 0 + className : '' + max : 100 + min : 0 + mode : 'indeterminate' + type : 'linear' + value : 0 + + # -- Helper methods + calculateRatio: (value) -> + (value - @props.min) / (@props.max - @props.min) + + # -- Render + render: -> + className = "#{@props.type} #{@props.className} #{@props.mode}" +
+ { if @props.type == 'circular' then @renderCircular() else @renderLinear() } +
+ + renderCircular: -> + style = transformDasharray(@calculateRatio(@props.value)) unless @props.mode == 'indeterminate' + + + + + renderLinear: -> + unless @props.mode == 'indeterminate' + bufferStyle = transformProgress(@calculateRatio(@props.buffer)) + valueStyle = transformProgress(@calculateRatio(@props.value)) +
+ + +
+ +# -- Private methods +transformDasharray = (ratio) -> + strokeDasharray: "#{2 * Math.PI * 45 * ratio}, 400" + +transformProgress = (ratio) -> + WebkitTransform: "scaleX(#{ratio})" + MsTransform: "scaleX(#{ratio})" + transform: "scaleX(#{ratio})" diff --git a/components/progress_bar/progress_bar.md b/components/progress_bar/progress_bar.md new file mode 100644 index 00000000..ccf86009 --- /dev/null +++ b/components/progress_bar/progress_bar.md @@ -0,0 +1,27 @@ +# Progress Bar + +```javascript +var ProgressBar = require('react-toolbox/components/progress_bar'); + +// Circular indeterminate progress bar + + +// Linear determinate progress bar with buffer + +``` + +## Properties + +| Name | Type | Default | Description| +| ------------- |:-------:|:--------------- |:---------- | +| **type** | String | `linear` | Type of the component, it can be *circular* or *linear*.| +| **mode** | String | `indeterminate` | Mode of the progress bar, it can be *determinate* or *indeterminate*.| +| **value** | Number | `0` | Value of the current progress.| +| **min** | Number | `0` | Minimum value permitted.| +| **max** | Number | `100` | Maximum value permitted.| +| **buffer** | Number | `0` | Value of a secondary progress bar useful for buffering.| +| **className** | String | `''` | Additional class name to provide custom styling.| + +## Methods + +This component has no methods. diff --git a/components/progress_bar/style.styl b/components/progress_bar/style.styl new file mode 100644 index 00000000..76bd4d02 --- /dev/null +++ b/components/progress_bar/style.styl @@ -0,0 +1,89 @@ +@import '../constants.styl' + +// -- NOTE: Parameters depend on the component SVG Markup too +CIRCLE_WRAPPER_WIDTH = 100 +CIRCLE_RADIUS = 45 +SCALE_RATIO = CIRCLE_RADIUS/20 + +[data-component-progressbar] + display : inline-block + + &.linear + position : relative + height : PROGRESS_BAR_HEIGHT + width : 100% + background : darken(BACKGROUND, 7.5%) + overflow : hidden + + [data-component-progressbar-value], [data-component-progressbar-buffer] + position : absolute + bottom : 0 + left : 0 + right : 0 + top : 0 + transform scaleX(0) + transform-origin left center + transition-duration ANIMATION_DURATION + transition-timing-function ANIMATION_EASE + + [data-component-progressbar-value] + background-color : SECONDARY + + [data-component-progressbar-buffer] + background-color : alpha(SECONDARY, 15%) + + &.indeterminate [data-component-progressbar-value] + transform-origin center center + animation linear-indeterminate-bar 1s linear infinite + + &.circular + position : relative + height : CIRCLE_WRAPPER_WIDTH * 1px + width : CIRCLE_WRAPPER_WIDTH * 1px + transform rotate(-90deg) + + [data-component-progressbar-circle] + height : 100% + width : 100% + + [data-component-progressbar-circle-path] + stroke-dasharray : 0, SCALE_RATIO * 200 + stroke-dashoffset : 0 + stroke-linecap : round + stroke-miterlimit : 20 + stroke-width : 4 + stroke : SECONDARY + fill : none + transition stroke-dasharray ANIMATION_DURATION ANIMATION_EASE + + &.indeterminate + [data-component-progressbar-circle] + animation circular-indeterminate-bar-rotate 2s linear infinite + + [data-component-progressbar-circle-path] + stroke-dasharray : SCALE_RATIO * 1, SCALE_RATIO * 200 + stroke-dashoffset : 0 + animation circular-indeterminate-bar-dash 1.5s ease-in-out infinite + +@keyframes linear-indeterminate-bar + 0% + transform translate(-50%) scaleX(0) + 50% + transform translate(-0%) scaleX(0.3) + 100% + transform translate(50%) scaleX(0) + +@keyframes circular-indeterminate-bar-rotate + 100% + transform : rotate(360deg) + +@keyframes circular-indeterminate-bar-dash + 0% + stroke-dasharray : SCALE_RATIO * 1, SCALE_RATIO * 200 + stroke-dashoffset : SCALE_RATIO * 0 + 50% + stroke-dasharray : SCALE_RATIO * 89, SCALE_RATIO * 200 + stroke-dashoffset : SCALE_RATIO * -35 + 100% + stroke-dasharray : SCALE_RATIO * 89, SCALE_RATIO * 200 + stroke-dashoffset : SCALE_RATIO * -124 diff --git a/spec/components/progress.cjsx b/spec/components/progress.cjsx new file mode 100644 index 00000000..0946e105 --- /dev/null +++ b/spec/components/progress.cjsx @@ -0,0 +1,48 @@ +### +@todo +### + +ProgressBar = require '../../components/progress_bar' + +module.exports = React.createClass + + # -- States & Properties + getInitialState: -> + progress: 0 + buffer: 10 + + # -- Lifecycle + componentWillMount: -> + @simulateProgress() + + # -- Simulate kind of a progress async function + simulateProgress: -> + setTimeout => + if @state.progress < 100 + @increaseProgress() + @increaseBuffer() if @state.progress > @state.buffer + else + @replaceState @getInitialState() + @simulateProgress() + , (Math.random() * 1 + 1) * 1000 + + increaseProgress: -> + @setState progress: Math.random() * 30 + @state.progress + + increaseBuffer: -> + @setState buffer: Math.random() * (100 - @state.progress) + @state.progress + + # -- Render + render: -> +
+

Progress bars

+ +

Determinate

+ + +

Indeterminate...

+ + +

Circular

+ +
diff --git a/spec/index.cjsx b/spec/index.cjsx index bea66306..77a99f9e 100644 --- a/spec/index.cjsx +++ b/spec/index.cjsx @@ -7,6 +7,7 @@ Button = require './components/button' Dialog = require './components/dialog' Dropdown = require './components/dropdown' Form = require './components/form' +Progress = require './components/progress' # React = require('react/addons') # TestUtils = React.addons.TestUtils @@ -30,6 +31,7 @@ Test = React.createClass