react-switch
Advanced tools
Comparing version 1.2.0 to 2.0.0-rc.1
@@ -7,2 +7,20 @@ # Changelog | ||
## [Unfinished] | ||
### Added | ||
- New prop boxShadow. It acts just like outline, but I called it boxShadow since that is the actual css attribute that is being controlled. | ||
- New props checkedIcon and uncheckedIcon. They have a checkmark and an x as default. Custom elements can be given as icons or the boolean value 'false', which will remove icons. | ||
### Refactor | ||
- Dependency on 'react-draggable' is removed and replaced with new drag logic. | ||
### Fixed | ||
- Glitch related to faulty 'inTransition' state fixed due to inTransition no longer existing. | ||
## Changed | ||
- Outline disappears when the users stops holding down the mouse. This is the same behaviour as google's switch-button implementation. | ||
- onChange callback function is now also triggered when enter key is pressed in violation of wai-aria checkbox spec. This is reasonably since it's in the toggle-button spec. | ||
## Removed | ||
- The deprecated 'name' and 'value' properties are removed. | ||
## [1.2.0 - 2017-09-29] | ||
@@ -9,0 +27,0 @@ ### Fixed |
@@ -17,3 +17,3 @@ 'use strict'; | ||
var _reactDraggable = require('react-draggable'); | ||
var _icons = require('./icons'); | ||
@@ -36,14 +36,19 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
_this.handleClick = _this.handleClick.bind(_this); | ||
_this.handleDragStart = _this.handleDragStart.bind(_this); | ||
_this.handleDrag = _this.handleDrag.bind(_this); | ||
_this.handleDragStop = _this.handleDragStop.bind(_this); | ||
_this.handleTransitionEnd = _this.handleTransitionEnd.bind(_this); | ||
_this.handleMouseDown = _this.handleMouseDown.bind(_this); | ||
_this.handleMouseMove = _this.handleMouseMove.bind(_this); | ||
_this.handleMouseUp = _this.handleMouseUp.bind(_this); | ||
_this.handleTouchStart = _this.handleTouchStart.bind(_this); | ||
_this.handleTouchMove = _this.handleTouchMove.bind(_this); | ||
_this.handleTouchEnd = _this.handleTouchEnd.bind(_this); | ||
_this.handleTouchCancel = _this.handleTouchCancel.bind(_this); | ||
_this.handleClick = _this.handleClick.bind(_this); | ||
_this.handleKeyDown = _this.handleKeyDown.bind(_this); | ||
_this.state = { | ||
left: props.checked ? props.width - props.height + 1 : 1, | ||
inTransition: false, | ||
startX: null, | ||
isDragging: false, | ||
focused: false | ||
hasOutline: false | ||
}; | ||
@@ -55,4 +60,4 @@ return _this; | ||
key: 'componentWillReceiveProps', | ||
value: function componentWillReceiveProps(nextProps) { | ||
var left = this.state.left; | ||
value: function componentWillReceiveProps(_ref) { | ||
var checked = _ref.checked; | ||
var _props = this.props, | ||
@@ -63,55 +68,18 @@ width = _props.width, | ||
var checkedLeft = width - height + 1; | ||
var newLeft = nextProps.checked ? checkedLeft : 1; | ||
/* !IMPORTANT: Don't set inTransition to true if the new and old left-position | ||
is the same since this will not trigger the transition */ | ||
if (left !== newLeft) { | ||
this.setState({ | ||
left: newLeft, | ||
inTransition: true | ||
}); | ||
} | ||
var left = checked ? checkedLeft : 1; | ||
this.setState({ left: left }); | ||
} | ||
}, { | ||
key: 'handleClick', | ||
value: function handleClick() { | ||
var _props2 = this.props, | ||
checked = _props2.checked, | ||
onChange = _props2.onChange; | ||
onChange(!checked); | ||
} | ||
}, { | ||
key: 'handleKeyDown', | ||
value: function handleKeyDown(event) { | ||
var _props3 = this.props, | ||
checked = _props3.checked, | ||
onChange = _props3.onChange; | ||
var isDragging = this.state.isDragging; | ||
// Trigger change only on spacebar key in accordance with wai-aria spec | ||
if (event.keyCode === 32 && !isDragging) { | ||
event.preventDefault(); | ||
onChange(!checked); | ||
} | ||
} | ||
}, { | ||
key: 'handleDragStart', | ||
value: function handleDragStart(event) { | ||
var inTransition = this.state.inTransition; | ||
if (inTransition) { | ||
return; | ||
} | ||
var clientX = event.clientX || event.touches[0].clientX; | ||
this.setState({ startX: clientX }); | ||
value: function handleDragStart(clientX) { | ||
this.setState({ startX: clientX, hasOutline: true }); | ||
} | ||
}, { | ||
key: 'handleDrag', | ||
value: function handleDrag(event) { | ||
var clientX = event.clientX || event.touches[0].clientX; | ||
var _props4 = this.props, | ||
checked = _props4.checked, | ||
width = _props4.width, | ||
height = _props4.height; | ||
value: function handleDrag(clientX) { | ||
var startX = this.state.startX; | ||
var _props2 = this.props, | ||
checked = _props2.checked, | ||
width = _props2.width, | ||
height = _props2.height; | ||
@@ -130,18 +98,13 @@ var checkedLeft = width - height + 1; | ||
left = _state.left, | ||
isDragging = _state.isDragging, | ||
inTransition = _state.inTransition; | ||
isDragging = _state.isDragging; | ||
var _props3 = this.props, | ||
checked = _props3.checked, | ||
onChange = _props3.onChange, | ||
width = _props3.width, | ||
height = _props3.height; | ||
if (inTransition) { | ||
return; | ||
} | ||
var _props5 = this.props, | ||
checked = _props5.checked, | ||
onChange = _props5.onChange, | ||
width = _props5.width, | ||
height = _props5.height; | ||
// Simulate clicking the handle | ||
if (!isDragging) { | ||
this.setState({ startX: null }); | ||
this.setState({ startX: null, hasOutline: false }); | ||
onChange(!checked); | ||
@@ -154,6 +117,6 @@ return; | ||
if (left > (checkedLeft + 1) / 2) { | ||
this.setState({ left: checkedLeft, startX: null, isDragging: false }); | ||
this.setState({ left: checkedLeft, startX: null, isDragging: false, hasOutline: false }); | ||
return; | ||
} | ||
this.setState({ startX: null, isDragging: false }); | ||
this.setState({ startX: null, isDragging: false, hasOutline: false }); | ||
onChange(false); | ||
@@ -163,14 +126,88 @@ return; | ||
if (left < (checkedLeft + 1) / 2) { | ||
this.setState({ left: 1, startX: null, isDragging: false }); | ||
this.setState({ left: 1, startX: null, isDragging: false, hasOutline: false }); | ||
return; | ||
} | ||
this.setState({ startX: null, isDragging: false }); | ||
this.setState({ startX: null, isDragging: false, hasOutline: false }); | ||
onChange(true); | ||
} | ||
}, { | ||
key: 'handleTransitionEnd', | ||
value: function handleTransitionEnd() { | ||
this.setState({ inTransition: false }); | ||
key: 'handleMouseDown', | ||
value: function handleMouseDown(event) { | ||
// Ignore right click and scroll | ||
if (typeof event.button === 'number' && event.button !== 0) { | ||
return; | ||
} | ||
this.handleDragStart(event.clientX); | ||
document.addEventListener('mousemove', this.handleMouseMove); | ||
document.addEventListener('mouseup', this.handleMouseUp); | ||
console.log('mousedown'); | ||
} | ||
}, { | ||
key: 'handleMouseMove', | ||
value: function handleMouseMove(event) { | ||
event.preventDefault(); | ||
this.handleDrag(event.clientX); | ||
console.log('mousemove'); | ||
} | ||
}, { | ||
key: 'handleMouseUp', | ||
value: function handleMouseUp() { | ||
this.handleDragStop(); | ||
document.removeEventListener('mousemove', this.handleMouseMove); | ||
document.removeEventListener('mouseup', this.handleMouseUp); | ||
console.log('mouseup'); | ||
} | ||
// TODO: Prevent mouse events from triggering on touch events. | ||
}, { | ||
key: 'handleTouchStart', | ||
value: function handleTouchStart(event) { | ||
console.log('touchstart'); | ||
this.handleDragStart(event.touches[0].clientX); | ||
} | ||
}, { | ||
key: 'handleTouchMove', | ||
value: function handleTouchMove(event) { | ||
console.log('touchmove'); | ||
this.handleDrag(event.touches[0].clientX); | ||
} | ||
}, { | ||
key: 'handleTouchEnd', | ||
value: function handleTouchEnd(event) { | ||
console.log('touchend'); | ||
event.preventDefault(); | ||
this.handleDragStop(); | ||
} | ||
}, { | ||
key: 'handleTouchCancel', | ||
value: function handleTouchCancel() { | ||
console.log('touchcancel'); | ||
this.setState({ startX: null, hasOutline: false }); | ||
} | ||
}, { | ||
key: 'handleClick', | ||
value: function handleClick() { | ||
var _props4 = this.props, | ||
checked = _props4.checked, | ||
onChange = _props4.onChange; | ||
onChange(!checked); | ||
} | ||
}, { | ||
key: 'handleKeyDown', | ||
value: function handleKeyDown(event) { | ||
var _props5 = this.props, | ||
checked = _props5.checked, | ||
onChange = _props5.onChange; | ||
var isDragging = this.state.isDragging; | ||
// Trigger change on spacebar and enter keys (in violation of wai-aria spec). | ||
if ((event.keyCode === 32 || event.keyCode === 13) && !isDragging) { | ||
event.preventDefault(); | ||
onChange(!checked); | ||
} | ||
} | ||
}, { | ||
key: 'render', | ||
@@ -188,7 +225,8 @@ value: function render() { | ||
activeHandleColor = _props6.activeHandleColor, | ||
checkedIcon = _props6.checkedIcon, | ||
uncheckedIcon = _props6.uncheckedIcon, | ||
boxShadow = _props6.boxShadow, | ||
height = _props6.height, | ||
width = _props6.width, | ||
id = _props6.id, | ||
name = _props6.name, | ||
value = _props6.value, | ||
ariaLabelledby = _props6['aria-labelledby'], | ||
@@ -200,3 +238,3 @@ ariaLabel = _props6['aria-label']; | ||
startX = _state2.startX, | ||
focused = _state2.focused; | ||
hasOutline = _state2.hasOutline; | ||
@@ -234,2 +272,3 @@ var checkedLeft = width - height + 1; | ||
background: startX ? activeHandleColor : handleColor, | ||
touchAction: 'none', | ||
WebkitTransition: isDragging ? null : 'left 0.2s ease-out', | ||
@@ -245,5 +284,19 @@ MozTransition: isDragging ? null : 'left 0.2s ease-out', | ||
outline: 0, | ||
boxShadow: focused ? '0px 0px 1px 2px #4D90FE' : null | ||
boxShadow: hasOutline ? boxShadow : null | ||
}; | ||
var uncheckedStyle = { | ||
position: 'absolute', | ||
right: 0, | ||
opacity: 1 - (left - 1) / (checkedLeft - 1), | ||
width: Math.min(height, width - height + 2), | ||
height: height, | ||
pointerEvents: 'none' | ||
}; | ||
var checkedStyle = { | ||
width: Math.min(height, width - height + 2), | ||
height: height, | ||
pointerEvents: 'none' | ||
}; | ||
return _react2.default.createElement( | ||
@@ -255,37 +308,44 @@ 'div', | ||
}, | ||
_react2.default.createElement('div', { | ||
className: 'react-switch-fg', | ||
style: foregroundStyle, | ||
onClick: disabled ? null : this.handleClick | ||
}), | ||
uncheckedIcon ? _react2.default.createElement( | ||
'div', | ||
{ style: uncheckedStyle }, | ||
uncheckedIcon | ||
) : null, | ||
_react2.default.createElement( | ||
_reactDraggable.DraggableCore, | ||
'div', | ||
{ | ||
onStart: this.handleDragStart, | ||
onDrag: this.handleDrag, | ||
onStop: this.handleDragStop, | ||
disabled: disabled | ||
className: 'react-switch-fg', | ||
style: foregroundStyle, | ||
onClick: disabled ? null : this.handleClick | ||
}, | ||
_react2.default.createElement('div', { | ||
role: 'checkbox', | ||
tabIndex: disabled ? null : 0, | ||
'aria-checked': checked, | ||
'aria-disabled': disabled, | ||
onTransitionEnd: this.handleTransitionEnd, | ||
onKeyDown: this.handleKeyDown, | ||
onFocus: function onFocus() { | ||
return _this2.setState({ focused: true }); | ||
}, | ||
onBlur: function onBlur() { | ||
return _this2.setState({ focused: false }); | ||
}, | ||
className: 'react-switch-handle', | ||
style: handleStyle, | ||
id: id, | ||
name: name, | ||
value: value, | ||
'aria-labelledby': ariaLabelledby, | ||
'aria-label': ariaLabel | ||
}) | ||
) | ||
checkedIcon ? _react2.default.createElement( | ||
'div', | ||
{ style: checkedStyle }, | ||
checkedIcon | ||
) : null | ||
), | ||
_react2.default.createElement('div', { | ||
role: 'checkbox', | ||
tabIndex: disabled ? null : 0, | ||
onMouseDown: disabled ? null : this.handleMouseDown, | ||
onTouchStart: disabled ? null : this.handleTouchStart, | ||
onTouchMove: disabled ? null : this.handleTouchMove, | ||
onTouchEnd: disabled ? null : this.handleTouchEnd, | ||
onTouchCancel: disabled ? null : this.handleTouchCancel, | ||
onKeyDown: this.handleKeyDown, | ||
onFocus: function onFocus() { | ||
return _this2.setState({ hasOutline: true }); | ||
}, | ||
onBlur: function onBlur() { | ||
return _this2.setState({ hasOutline: false }); | ||
}, | ||
onTransitionEnd: this.handleTransitionEnd, | ||
className: 'react-switch-handle', | ||
style: handleStyle, | ||
id: id, | ||
'aria-checked': checked, | ||
'aria-disabled': disabled, | ||
'aria-labelledby': ariaLabelledby, | ||
'aria-label': ariaLabel | ||
}) | ||
); | ||
@@ -307,10 +367,10 @@ } | ||
activeHandleColor: _propTypes2.default.string, | ||
checkedIcon: _propTypes2.default.oneOfType([_propTypes2.default.bool, _propTypes2.default.element]), | ||
uncheckedIcon: _propTypes2.default.oneOfType([_propTypes2.default.bool, _propTypes2.default.element]), | ||
boxShadow: _propTypes2.default.string, | ||
height: _propTypes2.default.number, | ||
width: _propTypes2.default.number, | ||
id: _propTypes2.default.string, | ||
name: _propTypes2.default.string, | ||
value: _propTypes2.default.string, | ||
'aria-labelledby': _propTypes2.default.string, | ||
'aria-label': _propTypes2.default.string | ||
}; | ||
@@ -324,2 +384,5 @@ | ||
activeHandleColor: '#ddd', | ||
checkedIcon: _icons.checkedIcon, | ||
uncheckedIcon: _icons.uncheckedIcon, | ||
boxShadow: '0px 0px 1px 2px #4D90FE', | ||
height: 28, | ||
@@ -329,4 +392,2 @@ width: 56, | ||
id: null, | ||
name: null, | ||
value: null, | ||
'aria-labelledby': null, | ||
@@ -333,0 +394,0 @@ 'aria-label': null |
{ | ||
"name": "react-switch", | ||
"version": "1.2.0", | ||
"version": "2.0.0-rc.1", | ||
"description": "Draggable toggle-switch component for react", | ||
@@ -12,5 +12,6 @@ "main": "dist/index.js", | ||
"prepublishOnly": "npm run build", | ||
"test": "jest --verbose", | ||
"test": "jest", | ||
"test:watch": "jest --watch", | ||
"start": "webpack-dev-server", | ||
"stats": "webpack --profile --json > stats.json" | ||
"stats": "webpack -p --profile --json > stats.json && webpack-bundle-analyzer stats.json examples/dist -s gzip" | ||
}, | ||
@@ -40,3 +41,5 @@ "repository": { | ||
"css-loader": "^0.28.7", | ||
"enzyme": "^2.9.1", | ||
"enzyme": "^3.0.0", | ||
"enzyme-adapter-react-16": "^1.0.0", | ||
"enzyme-to-json": "^3.0.1", | ||
"eslint": "^4.6.1", | ||
@@ -50,12 +53,11 @@ "eslint-config-airbnb": "^15.1.0", | ||
"prop-types": "^15.6.0", | ||
"raf": "^3.3.2", | ||
"react": "^16.0.0", | ||
"react-dom": "^16.0.0", | ||
"react-test-renderer": "^15.6.1", | ||
"react-test-renderer": "^16.0.0", | ||
"style-loader": "^0.18.2", | ||
"webpack": "^3.6.0", | ||
"webpack-bundle-analyzer": "^2.9.0", | ||
"webpack-dev-server": "^2.8.2" | ||
}, | ||
"dependencies": { | ||
"react-draggable": "^3.0.3" | ||
}, | ||
"keywords": [ | ||
@@ -62,0 +64,0 @@ "switch", |
# react-switch | ||
[![npm](https://img.shields.io/npm/v/react-switch.svg)]() | ||
[![npm](https://img.shields.io/npm/dm/react-switch.svg)]() | ||
[![npm](https://img.shields.io/npm/v/react-switch.svg)](https://www.npmjs.com/package/react-switch) | ||
[![npm](https://img.shields.io/npm/dm/react-switch.svg)](https://www.npmjs.com/package/react-switch) | ||
[![GitHub stars](https://img.shields.io/github/stars/yogaboll/react-switch.svg?style=social&label=Stars)](https://github.com/yogaboll/react-switch) | ||
@@ -5,0 +6,0 @@ A draggable, customizable and accessible toggle-switch component for React. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3
12
71
36423
26
633
1
1
- Removedreact-draggable@^3.0.3
- Removedclassnames@2.5.1(transitive)
- Removedreact-draggable@3.3.2(transitive)