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/dropdown/dropdown.md b/components/dropdown/doc.md
similarity index 50%
rename from components/dropdown/dropdown.md
rename to components/dropdown/doc.md
index 13afe746..b251e849 100644
--- a/components/dropdown/dropdown.md
+++ b/components/dropdown/doc.md
@@ -3,28 +3,33 @@
```
var DropDown = require('react-toolbox/components/dropdown');
-var data = [
- { '1': 'Never' },
- { '2': 'Every Night' },
- { '3': 'Weeknights' },
- { '4': 'Weekends' },
- { '5': 'Weekly' },
+var countries: [
+ value: 'ES-es', label: 'Spain', img: 'http://'
+,
+ value: 'TH-th', label: 'Thailand', img: 'http://'
+,
+ value: 'EN-gb', label: 'England', img: 'http://'
+,
+ value: 'EN-en', label: 'USA', img: 'http://'
+,
+ value: 'FR-fr', label: 'France', img: 'http://'
];
-
+
```
## Properties
-| Name | Type | Default | Description|
+| Name | Type | Default | Description |
|:- |:-: | :- |:-|
-| **className** | String | | Set the class-styles of the Component.|
-| **dataSource** | Object | | JSON data representing all items in the dropdown.|
-| **disabled** | Boolean | false | Set components disabled.|
-| **label** | String | | The text string to use for the floating label element.|
-| **onChange** | Function | | Callback function that is fired when the components's value changes.|
-| **value** | String | | Default value using JSON data.|
-| **type** | String | "normal" | Type of the component, overwrite this property if you need set a different stylesheet.|
+| **className** | String | | Set the class-styles of the Component.
+| **dataSource** | Array | | JSON data representing all items in the dropdown.
+| **disabled** | Boolean | false | Set components disabled.
+| **label** | String | | The text string to use for the floating label element.
+| **onChange** | Function | | Callback function that is fired when the components's value changes.
+| **template** | Function | | Callback function that represents a item for the component.
+| **type** | String | "normal" | Type of the component, overwrite this property if you need set a different stylesheet.
+| **value** | String | | Default value using JSON data.
## Methods
@@ -33,11 +38,13 @@ Returns the value of the input.
```
dropdown_instance.getValue();
+> 'TH-th'
```
#### setValue
Sets the value of the input element.
```
-dropdown_instance.setValue(newValue);
+var new_value = 'ES-es'
+dropdown_instance.setValue(new_value);
```
diff --git a/components/dropdown/index.cjsx b/components/dropdown/index.cjsx
index ff2664fb..47fbe557 100644
--- a/components/dropdown/index.cjsx
+++ b/components/dropdown/index.cjsx
@@ -12,23 +12,32 @@ module.exports = React.createClass
# -- States & Properties
propTypes:
className : React.PropTypes.string
- dataSource : React.PropTypes.object
+ dataSource : React.PropTypes.array
disabled : React.PropTypes.disabled
label : React.PropTypes.string
onChange : React.PropTypes.func
+ template : React.PropTypes.func
type : React.PropTypes.string
value : React.PropTypes.string
getDefaultProps: ->
className : ""
- dataSource : {}
+ dataSource : []
disabled : false
type : "normal"
getInitialState: ->
active : false
- value : @props.value or Object.keys(@props.dataSource)[0]
ripple : undefined
+ selected : _selectValue @props.value, @props.dataSource
+
+ # -- Lifecycle
+ componentDidMount: ->
+ @setState
+ height: @refs.values.getDOMNode().firstElementChild.getBoundingClientRect().height
+
+ componentDidUpdate: (prev_props, prev_state) ->
+ @props.onChange? @ if prev_state.selected isnt @state.selected and prev_state.active
# -- Events
onSelect: (event) ->
@@ -36,16 +45,17 @@ module.exports = React.createClass
onItem: (event) ->
unless @props.disabled
- target = event.target
- client = target.getBoundingClientRect?()
- @setState
- active : false
- value : target.getAttribute "id"
- ripple :
- left : event.pageX - client?.left
- top : event.pageY - client?.top
- width : (client?.width * 2)
- @props.onChange event, @
+ client = event.target.getBoundingClientRect?()
+ value = event.target.getAttribute("id").toString()
+ for item in @props.dataSource when item.value.toString() is value
+ @setState
+ active : false
+ selected : item
+ ripple :
+ left : event.pageX - client?.left
+ top : event.pageY - client?.top
+ width : (client?.width * 2)
+ break
# -- Render
render: ->
@@ -53,25 +63,41 @@ module.exports = React.createClass
className += " disabled" if @props.disabled
if @state.active is true
className += " active"
- stylesheet = height: @refs.value.getDOMNode().offsetHeight * Object.keys(@props.dataSource).length
+ stylesheet = height: @state.height * @props.dataSource.length
{
if @props.label }
-
# -- Extends
getValue: ->
- @state.value
+ @state.selected.value
setValue: (data) ->
- @setState value: data
+ @setState selected: data
+
+# -- Internal methods
+_selectValue = (value, dataSource) ->
+ if value
+ for item in dataSource when item.value.toString() is value.toString()
+ return item
+ break
+ else
+ dataSource[0]
diff --git a/components/dropdown/style.styl b/components/dropdown/style.styl
index 15065890..eb98d8dc 100644
--- a/components/dropdown/style.styl
+++ b/components/dropdown/style.styl
@@ -9,10 +9,10 @@
// -- Class
&.active
- > label, > span
+ > label, > div
opacity : 0
transform translateY((INPUT_HEIGHT / 4))
- > span:after
+ > div:after
transform scale(0)
> ul
opacity : 1
@@ -30,39 +30,23 @@
&.disabled
color : FORM_BORDER_COLOR
border-bottom-style : dotted
- > span:after
+ > div:after
transform scale(0)
// -- Children
> *
width : 100%
- > label, > span
+ > label, > div
transition-property opacity, transform
transition-duration ANIMATION_DURATION
transition-timing-function ANIMATION_EASE
+ > label + ul
+ top : SPACE
> label
position : relative
bottom : -(SPACE / 4)
font-size : FONT_SIZE_TINY
color : FORM_BORDER_COLOR
- > span
- display : block
- height : INPUT_HEIGHT
- line-height : INPUT_HEIGHT
- font-size : FONT_SIZE_NORMAL
- &:after
- SIZE = (INPUT_HEIGHT / 7)
- position : absolute
- content : ""
- right : (SPACE / 2)
- bottom : SPACE
- width : 0
- height : 0
- border-left : BORDER = SIZE solid transparent
- border-right : BORDER
- border-top : SIZE solid FORM_BORDER_COLOR
- transition transform ANIMATION_DURATION ANIMATION_EASE
-
> ul
z-index : 2
position : absolute
@@ -93,6 +77,24 @@
pointer-events : none
> [data-component-ripple]
background-color : lighten(FORM_BORDER_COLOR, 50%)
+ > div
+ display : block
+ > span
+ height : INPUT_HEIGHT
+ font-size : FONT_SIZE_NORMAL
+ line-height : INPUT_HEIGHT
+ > :not(span)
+ margin : (SPACE / 2) 0
+ &:after
+ SIZE = (INPUT_HEIGHT / 7)
+ position : absolute
+ content : ""
+ right : (SPACE / 2)
+ bottom : SPACE
+ width : 0
+ height : 0
+ border-left : BORDER = SIZE solid transparent
+ border-right : BORDER
+ border-top : SIZE solid FORM_BORDER_COLOR
+ transition transform ANIMATION_DURATION ANIMATION_EASE
- > label + ul
- top : SPACE
diff --git a/components/index.coffee b/components/index.coffee
index 7d034604..6400de41 100644
--- a/components/index.coffee
+++ b/components/index.coffee
@@ -14,4 +14,5 @@ module.exports =
Loading : require "./loading"
Navigation : require "./navigation"
Ripple : require "./ripple"
+ ProgressBar : require "./progress_bar"
# -- Tools
diff --git a/components/input/index.cjsx b/components/input/index.cjsx
index 8cf11f9c..5267fca8 100644
--- a/components/input/index.cjsx
+++ b/components/input/index.cjsx
@@ -11,21 +11,25 @@ module.exports = React.createClass
multiline : React.PropTypes.bool
onBlur : React.PropTypes.func
onChange : React.PropTypes.func
- onFocus : React.PropTypes.func
onKeyPress : React.PropTypes.func
+ onFocus : React.PropTypes.func
+ onBlur : React.PropTypes.func
required : React.PropTypes.bool
type : React.PropTypes.string
value : React.PropTypes.string
getDefaultProps: ->
- className : ""
- type : "text"
+ className : ''
+ disabled : false
+ multiline : false
+ required : false
+ type : 'text'
getInitialState: ->
- value : @props.value
checked : @props.value
error : @props.error
- touch : @props.type in ["checkbox", "radio"]
+ touch : @props.type in ['checkbox', 'radio']
+ value : @props.value
# -- Events
onChange: (event) ->
@@ -38,38 +42,38 @@ module.exports = React.createClass
# -- Render
render: ->
className = @props.className
- className += " disabled" if @props.disabled
- className += " error" if @state.error
- className += " touch" if @state.touch
- className += " radio" if @props.type is "radio"
- className += " checked" if @state.checked
+ className += ' disabled' if @props.disabled
+ className += ' error' if @state.error
+ className += ' touch' if @state.touch
+ className += ' radio' if @props.type is 'radio'
+ className += ' checked' if @state.checked
{
if @props.multiline
-
else
-
}
-
+
{ if @props.label }
- { {@state.error} if @state.error }
+ { {@state.error} if @state.error }
# -- Extends
getValue: ->
- @refs.input?.getDOMNode()[if @state.touch then "checked" else "value"]
+ @refs.input?.getDOMNode()[if @state.touch then 'checked' else 'value']
setValue: (data) ->
@setState value: data
- setError: (data = "Unknown error") ->
+ setError: (data = 'Unknown error') ->
@setState error: @props.error or data
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/dropdown.cjsx b/spec/components/dropdown.cjsx
index 39d89a08..827952a5 100644
--- a/spec/components/dropdown.cjsx
+++ b/spec/components/dropdown.cjsx
@@ -8,24 +8,50 @@ module.exports = React.createClass
# -- States & Properties
getInitialState: ->
- countries: ["Spain", "England", "USA", "Thailand", "Tongo", "Slovenia"]
- countries_obj:
- "ES-es" : "Spain"
- "TH-th" : "Thailand"
- "EN-gb" : "England"
- "EN-en" : "USA"
- "FR-fr" : "France"
+ countries: [
+ value: "ES-es", label: "Spain", img: "http://"
+ ,
+ value: "TH-th", label: "Thailand", img: "http://"
+ ,
+ value: "EN-gb", label: "England", img: "http://"
+ ,
+ value: "EN-en", label: "USA", img: "http://"
+ ,
+ value: "FR-fr", label: "France", img: "http://"
+ ]
+ selected: "TH-th"
+
+ # -- Events
+ onChange: (dropdown) ->
+ console.log "[DROPDOWN]", dropdown.getValue()
+
+ # -- Internal
+ customDropdownItem: (data) ->
+ style =
+ width : 32
+ height : 32
+ backgroundColor : '#ccc'
+ marginRight : 8
+
+
+
+
+ {data.label}
+ {data.value}
+
+
# -- Render
render: ->
- countries_selected = ["USA", "Tongo"]
- countries_obj_selected = "TH-th"
-
Dropdown
lorem ipsum...
-
-
-
+
+
+
+
diff --git a/spec/components/form.cjsx b/spec/components/form.cjsx
index 627e76b7..c97430d3 100644
--- a/spec/components/form.cjsx
+++ b/spec/components/form.cjsx
@@ -3,6 +3,7 @@
###
Form = require '../../components/form'
+Input = require '../../components/input'
module.exports = React.createClass
@@ -31,7 +32,7 @@ module.exports = React.createClass
,
ref: "girl_2", type: "radio", label: "Are you a girl_2?", value: false, disabled: true
,
- ref: "type_user", type: "dropdown", label: "Type of user", dataSource: {1: "Normal", 2: "Root"}
+ ref: "type_user", type: "dropdown", label: "Type of user", dataSource: [{value: 1, label: "Normal"}, {value: 2, label: "Root"}]
,
type: "submit", caption: "Send", style: "primary anchor", disabled: true
]
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
+
React.render , document.body