New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

react-slider

Package Overview
Dependencies
Maintainers
1
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

react-slider - npm Package Compare versions

Comparing version 0.1.2 to 0.2.1

LICENSE

10

package.json
{
"name": "react-slider",
"version": "0.1.2",
"description": "Slider component for React.js",
"version": "0.2.1",
"description": "Slider component for React",
"main": "react-slider.js",

@@ -20,6 +20,10 @@ "scripts": {

"author": "Michał Powaga <michalpowaga13@gmail.com>",
"contributors": [{
"name": "Florian Klampfer",
"email": "f.klampfer@gmail.com"
}],
"license": "MIT",
"dependencies": {
"react": "^0.11.1"
"react": "^0.12.1"
}
}

@@ -1,2 +0,2 @@

(function(root, factory) {
(function (root, factory) {
if (typeof define === 'function' && define.amd) {

@@ -9,29 +9,47 @@ define(['react'], factory);

}
}(this, function(React) {
}(this, function (React) {
var ReactSlider = React.createClass({ displayName: 'ReactSlider',
var ReactSlider = React.createClass({
displayName: 'ReactSlider',
propTypes: {
minValue: React.PropTypes.number,
maxValue: React.PropTypes.number,
min: React.PropTypes.number,
max: React.PropTypes.number,
step: 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,
barClassName: React.PropTypes.string,
withBars: React.PropTypes.bool,
disabled: React.PropTypes.bool,
onChange: React.PropTypes.func,
valuePropName: React.PropTypes.string
onChanged: React.PropTypes.func
},
getDefaultProps: function() {
getDefaultProps: function () {
return {
minValue: 0,
maxValue: 100,
min: 0,
max: 100,
step: 1,
defaultValue: 0,
orientation: 'horizontal',
valuePropName: 'sliderValue'
className: 'slider',
handleClassName: 'handle',
barClassName: 'bar',
disabled: false
};
},
getInitialState: function() {
getInitialState: function () {
return {
offset: 0,
lowerBound: 0,
_dragMoveCache: {}, // TODO: find better solution
_touchMoveCache: {}, // TODO: find better solution
upperBound: 0,

@@ -41,9 +59,42 @@ handleWidth: 0,

sliderMax: 0,
value: this.props.minValue
value: this._or(this.props.value, this.props.defaultValue)
};
},
componentDidMount: function() {
_or: function (v1, v2) {
var count = React.Children.count(this.props.children);
switch (count) {
case 0:
return or(v1, v2);
case size(v1):
return v1;
case size(v2):
return v2;
default:
if (size(v1) !== count || size(v2) !== count) {
console.warn("ReactSlider: Number of values does not match number of children.");
}
return linspace(this.props.min, this.props.max, count);
}
},
componentDidMount: function () {
window.addEventListener('resize', this._handleResize);
this._handleResize();
var value = map(this.state.value, this._trimAlignValue);
this.setState({value: value});
},
componentWillUnmount: function () {
window.removeEventListener('resize', this._handleResize);
},
getValue: function () {
return this.state.value
},
_handleResize: function () {
var slider = this.refs.slider.getDOMNode();
var handle = this.refs.handle.getDOMNode();
var handle = this.refs.handle0.getDOMNode();
var rect = slider.getBoundingClientRect();

@@ -56,82 +107,155 @@

var position = {
horizontal: { min: 'left', max: 'right' },
vertical: { min: 'top', max: 'bottom' }
}[this.props.orientation];
this.setState({
upperBound: slider[size] - handle[size],
handleWidth: handle[size],
sliderMin: rect[position.min],
sliderMax: rect[position.max] - handle[size],
sliderMin: rect[this._min()],
sliderMax: rect[this._max()] - handle[size]
});
},
render: function() {
var handleStyle = {
transform: 'translate' + this._axis() + '(' + this.state.offset + 'px)',
// let this element be the same size as its children.
display: 'inline-block'
_calcOffset: function (value) {
var ratio = (value - this.props.min) / (this.props.max - this.props.min);
return ratio * this.state.upperBound;
},
_buildHandleStyle: function (offset) {
var transform = 'translate' + this._axis() + '(' + offset + 'px)';
return {
WebkitTransform: transform,
MozTransform: transform,
msTransform: transform,
OTransform: transform,
transform: transform,
position: 'absolute'
}
},
_buildBarStyle: function (minMax) {
var obj = {
position: 'absolute'
};
obj[this._min()] = minMax.min;
obj[this._max()] = minMax.max;
return obj;
},
var userHandle = this.props.children;
userHandle.props[this.props.valuePropName] = this.state.value;
_getClosestIndex: function (clickOffset) {
var self = this;
return (
React.DOM.div({ ref: 'slider', className: this.props.className, onClick: this._onClick },
React.DOM.div({ ref: 'handle', style: handleStyle, onMouseDown: this._dragStart, onTouchMove: this._touchMove },
userHandle
)));
// TODO: No need to iterate all
return reduce(this.state.value, function (min, value, i) {
var minDist = min[1];
var offset = self._calcOffset(value);
var dist = Math.abs(clickOffset - offset);
return (dist < minDist) ? [i, dist] : min;
}, [-1, Number.MAX_VALUE])[0];
},
_onClick: function(e) {
_onClick: function (e) {
var position = e['page' + this._axis()];
// make center of handle appear under the cursor position
this._moveHandle(position - (this.state.handleWidth / 2));
var clickOffset = position - this.state.sliderMin;
var closestIndex = this._getClosestIndex(clickOffset);
this._moveHandle(closestIndex, position);
if (this.props.onChanged) {
this.props.onChanged(this.state.value);
}
},
_dragStart: function() {
document.addEventListener('mousemove', this._dragMove, false);
document.addEventListener('mouseup', this._dragEnd, false);
_dragStart: function (i) {
var self = this;
return function (e) {
document.addEventListener('mousemove', self._dragMove(i), false);
document.addEventListener('mouseup', self._dragEnd(i), false);
pauseEvent(e);
}
},
_dragMove: function(e) {
var position = e['page' + this._axis()];
this._moveHandle(position);
_dragMove: function (i) {
var self = this;
if (!this.state._dragMoveCache[i]) {
this.state._dragMoveCache[i] =
function (e) {
var position = e['page' + self._axis()];
self._moveHandle(i, position);
}
}
return this.state._dragMoveCache[i];
},
_dragEnd: function() {
document.removeEventListener('mousemove', this._dragMove, false);
document.removeEventListener('mouseup', this._dragEnd, false);
_dragEnd: function (i) {
var self = this;
return function () {
document.removeEventListener('mousemove', self._dragMove(i), false);
document.removeEventListener('mouseup', self._dragEnd(i), false);
if (self.props.onChanged) {
self.props.onChanged(this.state.value);
}
}
},
_touchMove: function(e) {
var last = e.changedTouches[e.changedTouches.length - 1];
var position = last['page' + this._axis()];
this._moveHandle(position);
e.preventDefault();
_onTouchEnd: function () {
if (this.props.onChanged) {
this.props.onChanged(this.state.value);
}
},
_moveHandle: function(position) {
_touchMove: function (i) {
var self = this;
if (!this.state._touchMoveCache[i]) {
this.state._touchMoveCache[i] =
function (e) {
var last = e.changedTouches[e.changedTouches.length - 1];
var position = last['page' + self._axis()];
self._moveHandle(i, position);
e.preventDefault();
}
}
return this.state._touchMoveCache[i];
},
_moveHandle: function (i, position) {
if (this.props.disabled) return;
position = position - (this.state.handleWidth / 2);
var lastValue = this.state.value;
var ratio = (position - this.state.sliderMin) / (this.state.sliderMax - this.state.sliderMin);
var value = ratio * (this.props.maxValue - this.props.minValue) + this.props.minValue;
var nextValue = map(this.state.value, function (value, j) {
if (i !== j) return value;
var nextValue = this._trimAlignValue(value);
var nextRatio = (nextValue - this.props.minValue) / (this.props.maxValue - this.props.minValue);
var nextOffset = nextRatio * this.state.upperBound;
var ratio = (position - this.state.sliderMin) / (this.state.sliderMax - this.state.sliderMin);
var nextValue = this._trimAlignValue(ratio * (this.props.max - this.props.min) + this.props.min);
this.setState({
value: nextValue,
offset: nextOffset
});
// TODO: DRY?
if (i > 0) {
var valueBefore = at(this.state.value, i - 1);
if (nextValue <= valueBefore) {
nextValue = this._trimAlignValue(valueBefore + this.props.step);
}
}
var changed = nextValue !== lastValue;
if (changed && this.props.onChange) {
this.props.onChange(nextValue);
if (i < size(this.state.value) - 1) {
var valueAfter = at(this.state.value, i + 1);
if (nextValue >= valueAfter) {
nextValue = this._trimAlignValue(valueAfter - this.props.step);
}
}
return nextValue;
}, this);
var changed = !is(nextValue, lastValue);
if (changed) {
this.setState({value: nextValue});
if (this.props.onChange) this.props.onChange(nextValue);
}
},
_axis: function() {
_axis: function () {
return {

@@ -143,20 +267,173 @@ 'horizontal': 'X',

_trimAlignValue: function(val) {
if (val <= this.props.minValue) val = this.props.minValue;
if (val >= this.props.maxValue) val = this.props.maxValue;
_min: function () {
return {
'horizontal': 'left',
'vertical': 'top'
}[this.props.orientation];
},
var valModStep = (val - this.props.minValue) % this.props.step;
_max: function () {
return {
'horizontal': 'right',
'vertical': 'bottom'
}[this.props.orientation];
},
_trimAlignValue: function (val) {
if (val <= this.props.min) val = this.props.min;
if (val >= this.props.max) val = this.props.max;
var valModStep = (val - this.props.min) % this.props.step;
var alignValue = val - valModStep;
if (Math.abs(valModStep) * 2 >= this.props.step) {
alignValue += (valModStep > 0) ? this.props.step : (- this.props.step);
alignValue += (valModStep > 0) ? this.props.step : (-this.props.step);
}
return parseFloat(alignValue.toFixed(5));
},
_renderHandle: function (styles) {
var self = this;
return function (child, i) {
return (
React.createElement('div', {
ref: 'handle' + i,
key: 'handle' + i,
className: self.props.handleClassName + ' ' + self.props.handleClassName + '-' + i,
style: at(styles, i),
onMouseDown: self._dragStart(i),
onTouchMove: self._touchMove(i),
onTouchEnd: self._onTouchEnd,
onClick: self._pauseEvent
},
child
)
);
}
},
_pauseEvent: function (e) {
pauseEvent(e);
},
_renderHandles: function (offset) {
var styles = map(offset, this._buildHandleStyle, this);
if (React.Children.count(this.props.children) > 0) {
return React.Children.map(this.props.children, this._renderHandle(styles), this);
} else {
return map(offset, function (offset, i) {
return this._renderHandle(styles)(null, i);
}, this);
}
},
_renderBar: function (i, offsetFrom, offsetTo) {
return (
React.createElement('div', {
key: 'bar' + i,
ref: 'bar' + i,
className: this.props.barClassName + ' ' + this.props.barClassName + '-' + i,
style: this._buildBarStyle({
min: offsetFrom,
max: this.state.upperBound - offsetTo
})
})
);
},
_renderBars: function (offset) {
var bars = [];
var lastIndex = size(offset) - 1;
bars.push(this._renderBar(0, 0, at(offset, 0)));
for (var i = 0; i < lastIndex; i++) {
bars.push(this._renderBar(i + 1, offset[i], offset[i + 1]));
}
bars.push(this._renderBar(lastIndex + 1, at(offset, lastIndex), this.state.upperBound));
return bars;
},
render: function () {
var value = this._or(this.props.value, this.state.value);
var offset = map(value, this._calcOffset, this);
var bars = this.props.withBars ? this._renderBars(offset) : null;
var handles = this._renderHandles(offset);
return (
React.createElement('div', {
ref: 'slider',
style: {position: 'relative'},
className: this.props.className,
onClick: this._onClick
},
bars,
handles
)
);
}
});
/**
* Prevent text selection while dragging.
* http://stackoverflow.com/questions/5429827/how-can-i-prevent-text-element-selection-with-cursor-drag
*/
function pauseEvent(e) {
if (e.stopPropagation) e.stopPropagation();
if (e.preventDefault) e.preventDefault();
e.cancelBubble = true;
e.returnValue = false;
return false;
}
/**
* A little "hack" to treat a value and an array of values equally.
*/
function map(v, f, context) {
return (v && v.map) ? v.map(f, context) : f.call(context, v, 0);
}
function reduce(v, f, init) {
return (v && v.reduce) ? v.reduce(f, init) : f(init, v, 0);
}
function size(v) {
return exists(v) ? (v.length ? v.length : 1) : 0;
}
function at(v, i) {
return (v && v.map) ? v[i] : v;
}
function is(a, b) {
return size(a) === size(b) &&
reduce(a, function (res, v, i) {
return res && v === at(b, i)
}, true);
}
function or(maybe, other) {
return exists(maybe) ? maybe : other;
}
function exists(maybe) {
return typeof maybe !== 'undefined'
}
function linspace(min, max, count) {
var range = (max - min) / (count - 1);
var res = [];
for (var i = 0; i < count; i++) {
res.push(range * i);
}
return res;
}
return ReactSlider;
}));
}));

@@ -0,10 +1,14 @@

# React Slider
CSS agnostic slider component for React.
See demo: [https://cell303.github.io/react-slider](https://cell303.github.io/react-slider/)
### Important Note
This is alpha release. Use with caution and hope.
This is an alpha release. Use with caution and hope.
### Installation
```
```sh
npm install react-slider

@@ -15,28 +19,35 @@ ```

#### Single slider:
Similar to `<input type="range" defaultValue={50} />`
```javascript
var Handle = React,createClass({
render: function() {
return <div className="handle">{this.props.sliderValue}</div>;
}
});
React.render(<ReactSlider defaultValue={50} />, document.body);
```
var slider = (
<ReactSlider className="slider">
<Handle />
</ReactSlider>
);
#### Double slider (with bars between the handles):
React.renderComponent(slider, document.body);
```javascript
React.render(<ReactSlider defaultValue={[0, 100]} withBars />, document.body);
```
Outputs following html:
#### Multi slider:
```html
<div class="slider">
<div style="/* some weird stuff */">
<!-- yes, this is your handle component -->
<div className="handle">0</div>
</div>
```javascript
React.render(<ReactSlider defaultValue={[0, 33, 67, 100]} withBars />, document.body);
```
#### Provide custom handles:
```javascript
React.render(
<ReactSlider withBars>
<div className="my-handle">1</div>
<div className="my-handle">2</div>
<div className="my-handle">3</div>
</ReactSlider>,
document.body
);
```
Now you can style it as you want. Checkout the ```examples``` directory to see how.

@@ -46,7 +57,7 @@

##### minValue {number} default: 0
##### min {number} default: 0
The minimum value of the slider.
##### maxValue {number} default: 100
##### max {number} default: 100

@@ -57,4 +68,18 @@ The maximum value of the slider

Value to be added or subtracted on each step the slider makes. Must be greater than zero. ```maxValue - minValue``` should be evenly divisible by the step value.
Value to be added or subtracted on each step the slider makes. Must be greater than zero.
```max - min``` should be evenly divisible by the step value.
##### defaultValue {number|array\<number\>} default: 0
Determines the initial positions of the handles.
Also determines the number of handles.
If a number is passed a slider with one handle will be rendered.
If an array is passed each value will determine the position of one handle.
The values in the array must be sorted.
##### value {number|array\<number\>}
Like `defaultValue` but for [controlled components](http://facebook.github.io/react/docs/forms.html#controlled-components).
##### orientation {string} default: 'horizontal'

@@ -64,2 +89,26 @@

##### className {string} default: 'slider'
The css class set on the slider node.
##### handleClassName {string} default: 'handle'
The css class set on each handle node.
In addition each handle will receive a numbered css class of the form `${handleClassName}-${i}`,
e.g. `handle-0`, `handle-1`, ...
##### barClassName {string} default: 'bar'
The css class set on the bars between the handles.
In addition bar fragment will receive a numbered css class of the form `${barClassName}-${i}`,
e.g. `bar-0`, `bar-1`, ...
##### withBars {boolean} default: false
If `true` bars between the handles will be rendered.
##### disabled {boolean} default: false
If `true` the handles can't be moved.
##### onChange {function}

@@ -70,9 +119,20 @@

```javascript
function onChange(value) {
console.log('New slider value: ' + value);
function onChange(value) {
console.log('New slider value: ' + value);
}
```
##### valuePropName {string} default: 'sliderValue'
##### onChanged {function}
Name of property which is passed to children component and contains current slider value.
Callback called only after dragging/touching has ended or when a new value is set by clicking on the slider.
### Methods
##### getValue
Returns the current value of the slider, which is a number in the case of a single slider,
or an array of numbers in case of a multi slider.
### License
See the [License](LICENSE) file.

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc