diff options
| author | Andrej Mihajlov <and@codeispoetry.ru> | 2017-02-17 15:17:20 +0000 |
|---|---|---|
| committer | Andrej Mihajlov <and@codeispoetry.ru> | 2017-02-17 15:17:20 +0000 |
| commit | 00b82cb3aa904b34d7000854bcecd3b72e012c8e (patch) | |
| tree | ac314579a84d6c5b6fbbda8f0a0984f023364870 /app/components | |
| parent | e88018b890c8ddf62895101439d57ca206d9379b (diff) | |
| download | mullvadvpn-00b82cb3aa904b34d7000854bcecd3b72e012c8e.tar.xz mullvadvpn-00b82cb3aa904b34d7000854bcecd3b72e012c8e.zip | |
Add switch control and hook up settings reducer & actions
Diffstat (limited to 'app/components')
| -rw-r--r-- | app/components/Settings.css | 2 | ||||
| -rw-r--r-- | app/components/Settings.js | 11 | ||||
| -rw-r--r-- | app/components/Switch.css | 44 | ||||
| -rw-r--r-- | app/components/Switch.js | 107 |
4 files changed, 161 insertions, 3 deletions
diff --git a/app/components/Settings.css b/app/components/Settings.css index 30186446e4..350ae406a5 100644 --- a/app/components/Settings.css +++ b/app/components/Settings.css @@ -101,7 +101,7 @@ } .settings__cell-value { - + flex: 0 0 auto; } .settings__cell-footer { diff --git a/app/components/Settings.js b/app/components/Settings.js index 5b36d8a64c..8089482f02 100644 --- a/app/components/Settings.js +++ b/app/components/Settings.js @@ -1,16 +1,23 @@ import React, { Component, PropTypes } from 'react'; import { Layout, Container, Header } from './Layout'; +import Switch from './Switch'; export default class Settings extends Component { static propTypes = { - logout: PropTypes.func.isRequired + logout: PropTypes.func.isRequired, + updateSettings: PropTypes.func.isRequired } onClose() { this.props.router.push('/connect'); } + handleAutoSecure(isOn) { + console.log('autoSecure: ' + isOn); + this.props.updateSettings({ autoSecure: isOn }); + } + render() { return ( <Layout> @@ -31,7 +38,7 @@ export default class Settings extends Component { <div className="settings__cell"> <div className="settings__cell-label">Auto-secure</div> <div className="settings__cell-value"> - <input type="checkbox" className="settings__switch" /> + <Switch onChange={ ::this.handleAutoSecure } isOn={ this.props.settings.autoSecure } /> </div> </div> <div className="settings__cell-footer"> diff --git a/app/components/Switch.css b/app/components/Switch.css new file mode 100644 index 0000000000..961312c103 --- /dev/null +++ b/app/components/Switch.css @@ -0,0 +1,44 @@ +.switch { + display: block; + position: relative; + -webkit-appearance: none; + border-radius: 16px; + width: 50px; + height: 30px; + border: 2px solid white; + background-color: transparent; + transition: 300ms ease-in-out all; +} + +.switch:checked { + text-align: right; +} + +.switch::after { + position: absolute; + left: 1px; + top: 1px; + display: block; + content: ''; + width: 24px; + height: 24px; + border-radius: 24px; + background-color: #D0021B; + transition: 300ms ease-in-out all; + transform: translate3d(0, 0, 0); +} + +.switch:active::after { + width: 28px; +} + +.switch:active:checked::after { + transform: translate3d(0, 0, 0); + left: 17px; +} + +.switch:checked::after { + background-color: #44AD4D; + transform: translate3d(0, 0, 0); + left: 21px; +}
\ No newline at end of file diff --git a/app/components/Switch.js b/app/components/Switch.js new file mode 100644 index 0000000000..16b64b2211 --- /dev/null +++ b/app/components/Switch.js @@ -0,0 +1,107 @@ +import React, { Component, PropTypes } from 'react'; + +export default class Switch extends Component { + + static propTypes = { + isOn: PropTypes.bool, + onChange: PropTypes.func + } + + constructor(props) { + super(props); + this.state = { + isTracking: false, + ignoreChange: false, + initialPos: null, + startTime: null, + target: null + }; + } + + handleMouseDown(e) { + const { pageX: x, pageY: y } = e; + this.setState({ + isTracking: true, + initialPos: { x, y }, + startTime: e.timeStamp + }); + } + + handleMouseMove(e) { + if(!this.state.isTracking) { + return; + } + + const thresholdX = 10, thresholdY = 50; + const { x: x0, y: y0 } = this.state.initialPos; + const { pageX: x, pageY: y } = e; + + const dx = Math.abs(x0 - x); + const dy = Math.abs(y0 - y); + + if(dx < thresholdX || dy > thresholdY) { + return; + } + + const isOn = !!this.props.isOn; + let nextOn = isOn; + + if(x < x0 && isOn) { + nextOn = false; + } else if(x > x0 && !isOn) { + nextOn = true; + } + + if(isOn !== nextOn) { + this.setState({ initialPos: { x, y } }); + this.refs.input.checked = nextOn; + this.notify(nextOn); + this.setState({ ignoreChange: true }); + } + } + + handleMouseUp() { + if(this.state.isTracking) { + this.setState({ isTracking: false, initialPos: null }); + console.log('mouseup'); + } + } + + handleChange(e) { + console.log('ONCHANGE ' + e.target.checked); + const delta = e.timeStamp - this.state.startTime; + const threshold = 1000; + + if(this.state.ignoreChange) { + e.preventDefault(); + this.setState({ ignoreChange: false }); + } else if(delta > threshold) { + e.preventDefault(); + } else { + this.notify(e.target.checked); + } + } + + notify(isOn) { + if(this.props.onChange) { + this.props.onChange(isOn); + } + } + + componentDidMount() { + document.addEventListener('mousemove', ::this.handleMouseMove); + document.addEventListener('mouseup', ::this.handleMouseUp); + } + + componentWillUnmount() { + document.removeEventListener('mousemove', ::this.handleMouseMove); + document.removeEventListener('mouseup', ::this.handleMouseUp); + } + + render() { + return ( + <input type="checkbox" ref="input" className="switch" checked={ this.props.isOn } + onMouseDown={ ::this.handleMouseDown } onChange={ ::this.handleChange } /> + ); + } +}
\ No newline at end of file |
