Created
September 8, 2015 15:24
-
-
Save samhains/41eb70c4faa4d016e8ac to your computer and use it in GitHub Desktop.
React components for slider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#= require ./diff-slider | |
#= require ./diff-panel | |
DiffSlider = window.Aeon.Components.DiffSlider | |
DiffPanel = window.Aeon.Components.DiffPanel | |
window.Aeon.Components.RevisionsContainer = React.createClass | |
setCurrDiff: (diff) -> | |
@_updateDiffPanel diff, diff+1 | |
componentWillMount: -> | |
@_updateDiffPanel @state.prevDiffIndex, @state.currDiffIndex | |
getInitialState: -> | |
currDiffIndex: @props.versions.length-1, | |
prevDiffIndex: @props.versions.length-2, | |
splitHandle: false, | |
disableHideSign: true, | |
showHandleInfo: true, | |
handleInfoPosition: 0, | |
prevDiff: '', | |
currDiff: '' | |
toggleHandle: -> | |
@setState | |
splitHandle: !@state.splitHandle | |
prevDiffIndex: @state.currDiffIndex-1 | |
updateDiffs: (e) -> | |
if @state.splitHandle | |
prevIndex = e[0] | |
currIndex = e[1] | |
else | |
currIndex = parseInt(e[0]) | |
prevIndex = currIndex-1 | |
@_updateDiffPanel prevIndex, currIndex | |
showSign: (index) -> | |
@setState | |
showHandleInfo: true | |
handleInfoPosition: index | |
hideSign: -> | |
@setState | |
showHandleInfo: false | |
editInformation: -> | |
currVersion = @props.versions[@state.currDiffIndex].version | |
<div className='revisions__info-container cf'> | |
{@_renderHandles()} | |
<input type="hidden" name="version_id" value={currVersion.id}/> | |
<button type="submit" className='button button--link revisions__restore-button'> Restore This Version</button> | |
</div> | |
_renderHandles: -> | |
prevDiffIndex = @state.prevDiffIndex | |
if @state.prevDiffIndex < 0 | |
prevDiffIndex = 0 | |
currVersion = @props.versions[@state.currDiffIndex].version | |
prevVersion = @props.versions[prevDiffIndex].version | |
if @state.splitHandle | |
<div> | |
{@versionInfoTemplate prevVersion, false, @state.prevDiffIndex} | |
{@versionInfoTemplate currVersion, true, @state.currDiffIndex} | |
</div> | |
else | |
@versionInfoTemplate currVersion, true, @state.currDiffIndex | |
returnPopupToCurrPosition: -> | |
@setState handleInfoPosition: @state.currDiffIndex | |
versionInfoTemplate: (version, isCurrentVersion, handleIndex) -> | |
created_at = moment(version.created_at) | |
time = moment(created_at).format('MMM D @ h:mm') | |
timeFromNow = moment(created_at, "YYYYMMDD").fromNow() | |
classStr = if isCurrentVersion then 'revisions__curr-version-info' else 'revisions__prev-version-info' | |
classStr += ' span-6' | |
event = version.event | |
if event == 'create' | |
event = 'Essay created' | |
<span className={classStr}> | |
{event} by {version.user.name} | |
<div> {timeFromNow} | |
<span className='revisions__small-time'> ({time}) </span></div> | |
</span> | |
_updateDiffPanel: (prevIndex, currIndex) -> | |
if prevIndex < 0 | |
prevIndex = 0 | |
prevVersion = @props.versions[prevIndex].version | |
currVersion = @props.versions[currIndex].version | |
$.ajax | |
url: "/editorial/essays/diff?prev_id=#{prevVersion.id}&curr_id=#{currVersion.id}", | |
type: "GET", | |
success: (data) => | |
@setState | |
prevDiffIndex: prevIndex | |
currDiffIndex: currIndex | |
currDiff: data.data[1] | |
prevDiff: data.data[0] | |
error: (data) => | |
console.log data | |
render: -> | |
barWidth = 1/(@props.versions.length)*100 | |
<div> | |
<span className='revisions__checkbox-text'>Compare Two Revisions | |
<input className='revisions__checkbox' onClick={@toggleHandle} type='checkbox'/> | |
</span> | |
<br/> | |
<br/> | |
<DiffSlider | |
versions={@props.versions} | |
updateDiffs={@updateDiffs} | |
splitHandle={@state.splitHandle} | |
versionInfoTemplate={@versionInfoTemplate} | |
returnPopupToCurrPosition={@returnPopupToCurrPosition} | |
handleInfoPos={@state.handleInfoPosition} | |
currDiffIndex={@state.currDiffIndex} | |
barWidth={barWidth} | |
prevDiffIndex={@state.prevDiffIndex} | |
showSign={@showSign} | |
/> {@editInformation()} <hr/> | |
<DiffPanel | |
prevDiff={@state.prevDiff} | |
currDiff={@state.currDiff}/> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.Aeon.Components.DiffPanel = React.createClass | |
render: -> | |
<div className="revisions-panel"> | |
<div className='revisions__prev-essay span-6'> | |
<div dangerouslySetInnerHTML={__html: @props.prevDiff} /> | |
</div> | |
<div className='revisions__next-essay span-6'> | |
<div dangerouslySetInnerHTML={__html: @props.currDiff} /> | |
</div> | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#= require ./slider | |
ReactSlider = window.Aeon.Components.ReactSlider | |
window.Aeon.Components.DiffSlider = React.createClass | |
renderSliderTemplate: (defaultValue, children) -> | |
<div id='slider' onMouseLeave={@props.hideSign}> | |
{@_renderHandlePopup()} | |
<ReactSlider withBars | |
defaultValue={defaultValue} | |
key={@props.splitHandle} | |
min={0} | |
showSign={@props.showSign} | |
numOfBars={@props.versions.length} | |
returnPopupToCurrPosition={@props.returnPopupToCurrPosition} | |
barWidth={@props.barWidth} | |
max={@props.versions.length-1} | |
onAfterChange={@props.updateDiffs} > | |
{children} | |
</ReactSlider> | |
</div> | |
renderSlider: (splitHandle) -> | |
if splitHandle | |
children = <span><div className="handle--P"></div><div className="handle--C"></div></span> | |
@renderSliderTemplate [@props.prevDiffIndex, @props.currDiffIndex], children.props.children | |
else | |
@renderSliderTemplate @props.currDiffIndex, null | |
_buildPopupStyle: -> | |
left = @props.handleInfoPos*@props.barWidth | |
left: (left+1)+'%' | |
_renderHandlePopup: -> | |
version = @props.versions[@props.handleInfoPos].version | |
<div | |
className='handle__popup-box' style={@_buildPopupStyle()} > | |
{@props.versionInfoTemplate(version, false, @props.handleInfoPos)} | |
</div> | |
render: -> | |
<div className='slider__container'> | |
{@renderSlider(@props.splitHandle)} | |
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pauseEvent = (e) -> | |
if e.stopPropagation | |
e.stopPropagation() | |
if e.preventDefault | |
e.preventDefault() | |
e.cancelBubble = true | |
e.returnValue = false | |
e.stopPropagation() | |
stopPropagation = (e) -> | |
if e.stopPropagation | |
e.stopPropagation() | |
e.cancelBubble = true | |
e.stopPropagation() | |
linspace = (min, max, count) -> | |
range = (max - min) / (count - 1) | |
res = [] | |
i = 0 | |
while i < count | |
res.push min + range * i | |
i++ | |
res | |
ensureArray = (x) -> | |
unless x? then [] else (if Array.isArray(x) then x else [ x ]) | |
undoEnsureArray = (x) -> | |
if x != null and x.length == 1 then x[0] else x | |
window.Aeon.Components.ReactSlider = React.createClass( | |
displayName: 'ReactSlider' | |
propTypes: | |
min: React.PropTypes.number | |
max: React.PropTypes.number | |
step: React.PropTypes.number | |
minDistance: React.PropTypes.number | |
defaultValue: React.PropTypes.oneOfType([ | |
React.PropTypes.number | |
React.PropTypes.arrayOf(React.PropTypes.number) | |
]) | |
value: React.PropTypes.oneOfType([ | |
React.PropTypes.number | |
React.PropTypes.arrayOf(React.PropTypes.number) | |
]) | |
orientation: React.PropTypes.oneOf([ | |
'horizontal' | |
'vertical' | |
]) | |
className: React.PropTypes.string | |
handleClassName: React.PropTypes.string | |
handleActiveClassName: React.PropTypes.string | |
withBars: React.PropTypes.bool | |
barClassName: React.PropTypes.string | |
pearling: React.PropTypes.bool | |
disabled: React.PropTypes.bool | |
snapDragDisabled: React.PropTypes.bool | |
invert: React.PropTypes.bool | |
onBeforeChange: React.PropTypes.func | |
onChange: React.PropTypes.func | |
onAfterChange: React.PropTypes.func | |
onSliderClick: React.PropTypes.func | |
getDefaultProps: -> | |
{ | |
barClassName: 'bar' | |
className: 'slider' | |
defaultValue: 0 | |
disabled: false | |
handleActiveClassName: 'active' | |
handleClassName: 'handle' | |
invert: false | |
max: 100 | |
min: 0 | |
sliderLength: 400 | |
numOfBars: 100 | |
barWidth: 4 | |
minDistance: 0 | |
orientation: 'horizontal' | |
pearling: false | |
snapDragDisabled: false | |
step: 1 | |
withBars: false | |
} | |
getInitialState: -> | |
value = @_or(ensureArray(@props.value), ensureArray(@props.defaultValue)) | |
# reused throughout the component to store results of iterations over `value` | |
@tempArray = value.slice() | |
zIndices = [] | |
i = 0 | |
while i < value.length | |
value[i] = @_trimAlignValue(value[i], @props) | |
zIndices.push i | |
i++ | |
{ | |
index: -1 | |
upperBound: 0 | |
sliderLength: 0 | |
value: value | |
zIndices: zIndices | |
} | |
componentWillReceiveProps: (newProps) -> | |
value = @_or(ensureArray(newProps.value), @state.value) | |
# ensure the array keeps the same size as `value` | |
@tempArray = value.slice() | |
i = 0 | |
while i < value.length | |
@state.value[i] = @_trimAlignValue(value[i], newProps) | |
i++ | |
if @state.value.length > value.length | |
@state.value.length = value.length | |
# If an upperBound has not yet been determined (due to the component being hidden | |
# during the mount event, or during the last resize), then calculate it now | |
if @state.upperBound == 0 | |
@_handleResize() | |
return | |
_or: (value, defaultValue) -> | |
count = React.Children.count(@props.children) | |
switch count | |
when 0 | |
if value.length > 0 then value else defaultValue | |
when value.length | |
value | |
when defaultValue.length | |
defaultValue | |
else | |
if value.length != count or defaultValue.length != count | |
console.warn @constructor.displayName + ': Number of values does not match number of children.' | |
linspace(@props.min, @props.max, count) | |
componentDidMount: -> | |
window.addEventListener 'resize', @_handleResize | |
@_handleResize() | |
componentWillUnmount: -> | |
window.removeEventListener 'resize', @_handleResize | |
getValue: -> | |
undoEnsureArray @state.value | |
_handleResize: -> | |
# setTimeout of 0 gives element enough time to have assumed its new size if it is being resized | |
window.setTimeout(=> | |
slider = @refs.slider.getDOMNode() | |
handle = @refs.handle0.getDOMNode() | |
rect = slider.getBoundingClientRect() | |
size = @_sizeKey() | |
sliderMax = rect[@_posMaxKey()] | |
sliderMin = rect[@_posMinKey()] | |
@setState | |
upperBound: slider[size] - (handle[size]) | |
sliderLength: Math.abs(sliderMax - sliderMin) | |
handleSize: handle[size] | |
sliderStart: if @props.invert then sliderMax else sliderMin | |
, 0) | |
_calcOffset: (value) -> | |
ratio = (value - (@props.min)) / (@props.max - (@props.min)) | |
ratio * @state.upperBound | |
_calcValue: (offset) -> | |
ratio = offset / @state.upperBound | |
ratio * (@props.max - (@props.min)) + @props.min | |
_buildHandleStyle: (offset, i, value) -> | |
style = | |
position: 'absolute' | |
width: @props.barWidth+'%' | |
willChange: if @state.index >= 0 then @_posMinKey() else '' | |
zIndex: @state.zIndices.indexOf(i) + 1 | |
style[@_posMinKey()] = ((value)*@props.barWidth) + '%' | |
style | |
_buildBarStyle: (i) -> | |
position: 'absolute' | |
left: i*@props.barWidth+'%' | |
width: @props.barWidth+'%' | |
_getClosestIndex: (pixelOffset) -> | |
minDist = Number.MAX_VALUE | |
closestIndex = -1 | |
value = @state.value | |
l = value.length | |
i = 0 | |
while i < l | |
offset = @_calcOffset(value[i]) | |
dist = Math.abs(pixelOffset - offset) | |
if dist < minDist | |
minDist = dist | |
closestIndex = i | |
i++ | |
closestIndex | |
_calcOffsetFromPosition: (position) -> | |
pixelOffset = position - (@state.sliderStart) | |
if @props.invert | |
pixelOffset = @state.sliderLength - pixelOffset | |
pixelOffset -= @state.handleSize / 2 | |
pixelOffset | |
_forceValueFromPosition: (position, callback) -> | |
pixelOffset = @_calcOffsetFromPosition(position) | |
closestIndex = @_getClosestIndex(pixelOffset) | |
nextValue = @_trimAlignValue(@_calcValue(pixelOffset)) | |
value = @state.value.slice() | |
# Clone this.state.value since we'll modify it temporarily | |
value[closestIndex] = nextValue | |
# Prevents the slider from shrinking below `props.minDistance` | |
i = 0 | |
while i < value.length - 1 | |
if value[i + 1] - (value[i]) < @props.minDistance | |
return | |
i += 1 | |
@setState { value: value }, callback.bind(this, closestIndex) | |
_getMousePosition: (e) -> | |
[ | |
e['page' + @_axisKey()] | |
e['page' + @_orthogonalAxisKey()] | |
] | |
_getTouchPosition: (e) -> | |
touch = e.touches[0] | |
[ | |
touch['page' + @_axisKey()] | |
touch['page' + @_orthogonalAxisKey()] | |
] | |
_getMouseEventMap: -> | |
{ | |
'mousemove': @_onMouseMove | |
'mouseup': @_onMouseUp | |
} | |
_getTouchEventMap: -> | |
{ | |
'touchmove': @_onTouchMove | |
'touchend': @_onTouchEnd | |
} | |
_createOnMouseDown: (i) -> | |
(e) => | |
if @props.disabled | |
return | |
position = @_getMousePosition(e) | |
@_start i, position[0] | |
@_addHandlers @_getMouseEventMap() | |
pauseEvent e | |
_createOnTouchStart: (i) -> | |
(e) => | |
if @props.disabled or e.touches.length > 1 | |
return | |
position = @_getTouchPosition(e) | |
@startPosition = position | |
@isScrolling = undefined | |
# don't know yet if the user is trying to scroll | |
@_start i, position[0] | |
@_addHandlers @_getTouchEventMap() | |
stopPropagation e | |
_addHandlers: (eventMap) -> | |
for key of eventMap | |
document.addEventListener key, eventMap[key], false | |
_removeHandlers: (eventMap) -> | |
for key of eventMap | |
document.removeEventListener key, eventMap[key], false | |
_start: (i, position) -> | |
# if activeElement is body window will lost focus in IE9 | |
if document.activeElement and document.activeElement != document.body | |
document.activeElement.blur() | |
@hasMoved = false | |
@_fireChangeEvent 'onBeforeChange' | |
zIndices = @state.zIndices | |
zIndices.splice zIndices.indexOf(i), 1 | |
# remove wherever the element is | |
zIndices.push i | |
# add to end | |
@setState | |
startValue: @state.value[i] | |
startPosition: position | |
index: i | |
zIndices: zIndices | |
_onMouseUp: -> | |
@_onEnd @_getMouseEventMap() | |
_onTouchEnd: -> | |
@_onEnd @_getTouchEventMap() | |
_onEnd: (eventMap) -> | |
@_removeHandlers eventMap | |
@setState { index: -1 }, @_fireChangeEvent.bind(this, 'onAfterChange') | |
_onMouseMove: (e) -> | |
position = @_getMousePosition(e) | |
@_move position[0] | |
_onTouchMove: (e) -> | |
if e.touches.length > 1 | |
return | |
position = @_getTouchPosition(e) | |
if typeof @isScrolling == 'undefined' | |
diffMainDir = position[0] - (@startPosition[0]) | |
diffScrollDir = position[1] - (@startPosition[1]) | |
@isScrolling = Math.abs(diffScrollDir) > Math.abs(diffMainDir) | |
if @isScrolling | |
@setState index: -1 | |
return | |
pauseEvent e | |
@_move position[0] | |
_move: (position) -> | |
@hasMoved = true | |
index = @state.index | |
value = @state.value | |
length = value.length | |
oldValue = value[index] | |
diffPosition = position - (@state.startPosition) | |
# if @props.invert | |
# diffPosition *= -1 | |
diffValue = diffPosition / (@state.sliderLength - (@state.handleSize)) * (@props.max - (@props.min)) | |
newValue = @_trimAlignValue(@state.startValue + diffValue) | |
minDistance = @props.minDistance | |
# if "pearling" (= handles pushing each other) is disabled, | |
# prevent the handle from getting closer than `minDistance` to the previous or next handle. | |
if !@props.pearling | |
if index > 0 | |
valueBefore = value[index - 1] | |
if newValue < valueBefore + minDistance | |
newValue = valueBefore + minDistance | |
if index < length - 1 | |
valueAfter = value[index + 1] | |
if newValue > valueAfter - minDistance | |
newValue = valueAfter - minDistance | |
value[index] = newValue | |
#if "pearling" is enabled, let the current handle push the pre- and succeeding handles. | |
if @props.pearling and length > 1 | |
if newValue > oldValue | |
@_pushSucceeding value, minDistance, index | |
@_trimSucceeding length, value, minDistance, @props.max | |
else if newValue < oldValue | |
@_pushPreceding value, minDistance, index | |
@_trimPreceding length, value, minDistance, @props.min | |
# Normally you would use `shouldComponentUpdate`, but since the slider is a low-level component, | |
# the extra complexity might be worth the extra performance. | |
if newValue != oldValue | |
@props.showSign(value[index]) | |
@setState { value: value }, @_fireChangeEvent.bind(this, 'onChange') | |
_pushSucceeding: (value, minDistance, index) -> | |
i = undefined | |
padding = undefined | |
i = index | |
padding = value[i] + minDistance | |
while value[i + 1] != null and padding > value[i + 1] | |
value[i + 1] = @_alignValue(padding) | |
i++ | |
padding = value[i] + minDistance | |
return | |
_trimSucceeding: (length, nextValue, minDistance, max) -> | |
i = 0 | |
while i < length | |
padding = max - (i * minDistance) | |
if nextValue[length - 1 - i] > padding | |
nextValue[length - 1 - i] = padding | |
i++ | |
return | |
_pushPreceding: (value, minDistance, index) -> | |
i = undefined | |
padding = undefined | |
i = index | |
padding = value[i] - minDistance | |
while value[i - 1] != null and padding < value[i - 1] | |
value[i - 1] = @_alignValue(padding) | |
i-- | |
padding = value[i] - minDistance | |
return | |
_trimPreceding: (length, nextValue, minDistance, min) -> | |
i = 0 | |
while i < length | |
padding = min + i * minDistance | |
if nextValue[i] < padding | |
nextValue[i] = padding | |
i++ | |
return | |
_axisKey: -> | |
orientation = @props.orientation | |
if orientation == 'horizontal' | |
return 'X' | |
if orientation == 'vertical' | |
return 'Y' | |
return | |
_orthogonalAxisKey: -> | |
orientation = @props.orientation | |
if orientation == 'horizontal' | |
return 'Y' | |
if orientation == 'vertical' | |
return 'X' | |
return | |
_posMinKey: -> | |
orientation = @props.orientation | |
if orientation == 'horizontal' | |
return if @props.invert then 'right' else 'left' | |
if orientation == 'vertical' | |
return if @props.invert then 'bottom' else 'top' | |
return | |
_posMaxKey: -> | |
orientation = @props.orientation | |
if orientation == 'horizontal' | |
return if @props.invert then 'left' else 'right' | |
if orientation == 'vertical' | |
return if @props.invert then 'top' else 'bottom' | |
return | |
_sizeKey: -> | |
orientation = @props.orientation | |
if orientation == 'horizontal' | |
return 'clientWidth' | |
if orientation == 'vertical' | |
return 'clientHeight' | |
return | |
_trimAlignValue: (val, props) -> | |
@_alignValue @_trimValue(val, props), props | |
_trimValue: (val, props) -> | |
props = props or @props | |
if val <= props.min | |
val = props.min | |
if val >= props.max | |
val = props.max | |
val | |
_alignValue: (val, props) -> | |
props = props or @props | |
valModStep = (val - (props.min)) % props.step | |
alignValue = val - valModStep | |
if Math.abs(valModStep) * 2 >= props.step | |
alignValue += if valModStep > 0 then props.step else -props.step | |
parseFloat alignValue.toFixed(5) | |
_handleMouseEnter: (handleId) -> | |
@props.showSign(@state.value[handleId]) | |
_renderHandle: (style, child, i) -> | |
className = @props.handleClassName + ' ' + @props.handleClassName + '-' + i + ' ' + (if @state.index == i then @props.handleActiveClassName else '') | |
React.createElement 'div', { | |
ref: 'handle' + i | |
key: 'handle' + i | |
className: className | |
style: style | |
onMouseEnter: @_handleMouseEnter.bind(@, i) | |
onMouseDown: @_createOnMouseDown i | |
onTouchStart: @_createOnTouchStart i | |
}, child | |
_renderHandles: (offset, value) -> | |
length = offset.length | |
styles = @tempArray | |
i = 0 | |
while i < length | |
styles[i] = @_buildHandleStyle(offset[i], i, value[i]) | |
i++ | |
res = @tempArray | |
renderHandle = @_renderHandle | |
if React.Children.count(@props.children) > 0 | |
React.Children.forEach @props.children, (child, i) -> | |
res[i] = renderHandle(styles[i], child, i) | |
return | |
else | |
i = 0 | |
while i < length | |
res[i] = renderHandle(styles[i], null, i) | |
i++ | |
res | |
_barMouseEnter : (index) -> | |
@props.showSign(index) | |
_renderBar: (i) -> | |
React.createElement 'div', | |
#key: 'bar' + i | |
ref: 'bar' + i | |
onMouseLeave: @props.returnPopupToCurrPosition | |
onMouseEnter: @_barMouseEnter.bind(@, i) | |
className: @props.barClassName + ' ' + @props.barClassName + '-' + i | |
style: @_buildBarStyle i | |
_renderBars: (offset) -> | |
[0...@props.numOfBars].map (i) => | |
@_renderBar(i) | |
_onSliderMouseDown: (e) -> | |
if @props.disabled | |
return | |
@hasMoved = false | |
if !@props.snapDragDisabled | |
position = @_getMousePosition(e) | |
@_forceValueFromPosition position[0], (i) => | |
@_fireChangeEvent 'onChange' | |
@_start i, position[0] | |
@_addHandlers @_getMouseEventMap() | |
pauseEvent e | |
return | |
_onSliderClick: (e) -> | |
if @props.disabled | |
return | |
if @props.onSliderClick and !@hasMoved | |
position = @_getMousePosition(e) | |
calcOffset = @_calcOffsetFromPosition(position[0]) | |
calcValue = @_calcValue(calcOffset) | |
valueAtPos = @_trimAlignValue(calcValue) | |
@props.onSliderClick valueAtPos | |
return | |
_fireChangeEvent: (event) -> | |
if @props[event] | |
@props[event](@state.value) | |
render: -> | |
state = @state | |
props = @props | |
offset = @tempArray | |
value = state.value | |
l = value.length | |
i = 0 | |
while i < l | |
offset[i] = @_calcOffset(value[i], i) | |
i++ | |
bars = if props.withBars then @_renderBars(offset) else null | |
handles = @_renderHandles(offset, value) | |
React.createElement 'div', { | |
ref: 'slider' | |
style: position: 'relative' | |
className: props.className + (if props.disabled then ' disabled' else '') | |
onMouseDown: @_onSliderMouseDown | |
onClick: @_onSliderClick | |
}, bars, handles | |
) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment