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 } -
    +
      { - for key, label of @props.dataSource -
    • - {label} - {  if key is @state.value } + for item in @props.dataSource +
    • + { if @props.template then @props.template item else item.label } + {  if item.value is @state.selected.value }
    • }
    - {@props.dataSource[@state.value]} +
    + { + if @props.template + @props.template @state.selected + else + {@state.selected.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