Adding switches.

master
Sérgio Gomes 2015-01-08 11:57:35 +00:00
parent 3eb10ba174
commit d2439df4cf
6 changed files with 465 additions and 0 deletions

View File

@ -101,6 +101,11 @@
<iframe src="./styleguide/checkbox/demo.html" scrolling="no"></iframe>
</div>
<div class="styleguide-demo">
<h1>Switch</h1>
<iframe src="./styleguide/switch/demo.html" scrolling="no"></iframe>
</div>
<div class="styleguide-demo">
<h1>Slider</h1>
<iframe src="./styleguide/slider/demo.html" scrolling="no"></iframe>

View File

@ -83,6 +83,17 @@ $checkbox-color: nth($palette-primary, 6);
$checkbox-off-color: rgba(0, 0, 0, 0.54);
$checkbox-disabled-color: rgba(0, 0, 0, 0.26);
/* ========== Switches ========== */
$switch-color: nth($palette-primary, 6);
$switch-thumb-color: $switch-color;
$switch-track-color: rgba($switch-color, 0.5);
$switch-off-thumb-color: nth($palette-grey, 1);
$switch-off-track-color: rgba(0, 0, 0, 0.26);
$switch-disabled-thumb-color: nth($palette-grey, 5);
$switch-disabled-track-color: rgba(0, 0, 0, 0.12);
/* ========== Text fields ========== */
$input-text-background-color: transparent;

View File

@ -0,0 +1,181 @@
@import "../colors";
@import "../animation/animation";
@import "../shadow/shadow";
@import "../ripple/ripple";
$switch-label-height: 24px;
$switch-track-height: 14px;
$switch-track-length: 36px;
$switch-thumb-size: 20px;
$switch-track-top: ($switch-label-height - $switch-track-height) / 2;
$switch-thumb-top: ($switch-label-height - $switch-thumb-size) / 2;
$switch-ripple-size: $switch-label-height * 2;
.wsk-switch {
position: relative;
z-index: 1;
vertical-align: middle;
display: inline-block;
box-sizing: border-box;
width: 100%;
height: $switch-label-height;
margin: 12px 0;
padding: 0;
overflow: visible;
}
.wsk-switch__input {
line-height: $switch-label-height;
.wsk-switch.is-upgraded & {
// Hide input element, while still making it respond to focus.
position: absolute;
width: 0;
height: 0;
margin: 0;
padding: 0;
opacity: 0;
-ms-appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
border: none;
}
}
.wsk-switch__track {
background: $switch-off-track-color;
position: absolute;
left: 0;
top: $switch-track-top;
height: $switch-track-height;
width: $switch-track-length;
border-radius: $switch-track-height;
cursor: pointer;
.wsk-switch.is-checked & {
background: $switch-track-color;
}
.wsk-switch.is-disabled & {
background: $switch-disabled-track-color;
cursor: auto;
}
}
.wsk-switch__thumb {
background: $switch-off-thumb-color;
position: absolute;
left: 0;
top: $switch-thumb-top;
height: $switch-thumb-size;
width: $switch-thumb-size;
border-radius: 50%;
cursor: pointer;
@include shadow-z1();
@include material-animation-default(0.28s);
transition-property: left;
.wsk-switch.is-checked & {
background: $switch-thumb-color;
left: $switch-track-length - $switch-thumb-size;
@include shadow-z2();
}
.wsk-switch.is-disabled & {
background: $switch-disabled-thumb-color;
cursor: auto;
}
}
.wsk-switch__focus-helper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-$switch-button-size / 2, -$switch-button-size / 2);
display: inline-block;
box-sizing: border-box;
width: $switch-button-size;
height: $switch-button-size;
border-radius: 50%;
background-color: transparent;
.wsk-switch.is-focused & {
box-shadow: 0 0 0px (($switch-ripple-size - $switch-button-size) / 2)
rgba(0, 0, 0, 0.1);
background-color: rgba(0, 0, 0, 0.1);
}
.wsk-switch.is-focused.is-checked & {
box-shadow: 0 0 0px (($switch-ripple-size - $switch-button-size) / 2)
rgba($switch-color, 0.26);
background-color: rgba($switch-color, 0.26);
}
}
.wsk-switch__label {
position: relative;
cursor: pointer;
font-size: 16px;
line-height: $switch-label-height;
margin: 0;
left: 24px;
.wsk-switch.is-disabled & {
color: $switch-disabled-thumb-color;
cursor: auto;
}
}
.wsk-switch__ripple-container {
position: absolute;
z-index: 2;
top: -($switch-ripple-size - $switch-label-height) / 2;
left: $switch-thumb-size / 2 - $switch-ripple-size / 2;
box-sizing: border-box;
width: $switch-ripple-size;
height: $switch-ripple-size;
border-radius: 50%;
cursor: pointer;
overflow: hidden;
-webkit-mask-image: -webkit-radial-gradient(circle, white, black);
transition-duration: 0.40s;
transition-timing-function: step-end;
transition-property: left;
& .wsk-ripple {
background: $switch-color;
}
.wsk-switch.is-disabled & {
cursor: auto;
}
.wsk-switch.is-disabled & .wsk-ripple {
background: transparent;
}
.wsk-switch.is-checked & {
cursor: auto;
left: $switch-track-length - $switch-ripple-size / 2 -
$switch-thumb-size / 2;
}
}

View File

@ -0,0 +1,46 @@
<!doctype html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Switch</title>
<link href='//fonts.googleapis.com/css?family=RobotoDraft:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="demo.css">
</head>
<body>
<div class="PreviewBlock">
<label class="wsk-switch wsk-js-switch wsk-js-ripple-effect" for="switch-1">
<input type="checkbox" id="switch-1" class="wsk-switch__input" />
<span class="wsk-switch__label">Switch me</span>
</label>
<label class="wsk-switch wsk-js-switch wsk-js-ripple-effect" for="switch-2">
<input type="checkbox" id="switch-2" class="wsk-switch__input" />
<span class="wsk-switch__label">Flip me left and right</span>
</label>
<label class="wsk-switch wsk-js-switch wsk-js-ripple-effect" for="switch-4">
<input type="checkbox" id="switch-4" class="wsk-switch__input" />
<span class="wsk-switch__label">Me too</span>
</label>
<label class="wsk-switch wsk-js-switch wsk-js-ripple-effect" for="switch-5">
<input type="checkbox" id="switch-5" class="wsk-switch__input" disabled />
<span class="wsk-switch__label">Can't touch this</span>
</label>
</div>
<!-- build:js(app/styleguide/checkbox/) ../../scripts/main.min.js -->
<script src="../wskComponentHandler.js"></script>
<script src="switch.js"></script>
<script src="../third_party/rAF.js"></script>
<script src="../ripple/ripple.js"></script>
<!-- endbuild -->
</body>
</html>

View File

@ -0,0 +1,2 @@
@import "../styleguide_demo_bp";
@import "_switch";

View File

@ -0,0 +1,220 @@
/**
* Class constructor for Checkbox WSK component.
* Implements WSK component design pattern defined at:
* https://github.com/jasonmayes/wsk-component-design-pattern
* @param {HTMLElement} element The element that will be upgraded.
*/
function MaterialSwitch(element) {
'use strict';
this.element_ = element;
// Initialize instance.
this.init();
}
/**
* Store constants in one place so they can be updated easily.
* @enum {string | number}
* @private
*/
MaterialSwitch.prototype.Constant_ = {
TINY_TIMEOUT: 0.001
};
/**
* Store strings for class names defined by this component that are used in
* JavaScript. This allows us to simply change it in one place should we
* decide to modify at a later date.
* @enum {string}
* @private
*/
MaterialSwitch.prototype.CssClasses_ = {
/**
* Class names should use camelCase and be prefixed with the word "material"
* to minimize conflict with 3rd party systems.
*/
// TODO: Upgrade classnames in HTML / CSS / JS to use material prefix to
// reduce conflict and convert to camelCase for consistency.
WSK_SWITCH_INPUT: 'wsk-switch__input',
WSK_SWITCH_TRACK: 'wsk-switch__track',
WSK_SWITCH_THUMB: 'wsk-switch__thumb',
WSK_SWITCH_FOCUS_HELPER: 'wsk-switch__focus-helper',
WSK_JS_RIPPLE_EFFECT: 'wsk-js-ripple-effect',
WSK_JS_RIPPLE_EFFECT_IGNORE_EVENTS: 'wsk-js-ripple-effect--ignore-events',
WSK_SWITCH_RIPPLE_CONTAINER: 'wsk-switch__ripple-container',
WSK_RIPPLE_CENTER: 'wsk-ripple--center',
WSK_RIPPLE: 'wsk-ripple',
IS_FOCUSED: 'is-focused',
IS_DISABLED: 'is-disabled',
IS_CHECKED: 'is-checked'
};
/**
* Handle change of state.
* @param {Event} event The event that fired.
* @private
*/
MaterialSwitch.prototype.onChange_ = function(event) {
'use strict';
this.updateClasses_(this.btnElement_, this.element_);
};
/**
* Handle focus of element.
* @param {Event} event The event that fired.
* @private
*/
MaterialSwitch.prototype.onFocus_ = function(event) {
'use strict';
this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
};
/**
* Handle lost focus of element.
* @param {Event} event The event that fired.
* @private
*/
MaterialSwitch.prototype.onBlur_ = function(event) {
'use strict';
this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
};
/**
* Handle mouseup.
* @param {Event} event The event that fired.
* @private
*/
MaterialSwitch.prototype.onMouseUp_ = function(event) {
'use strict';
this.blur_();
};
/**
* Handle class updates.
* @param {HTMLElement} button The button whose classes we should update.
* @param {HTMLElement} label The label whose classes we should update.
* @private
*/
MaterialSwitch.prototype.updateClasses_ = function(button, label) {
'use strict';
if (button.disabled) {
label.classList.add(this.CssClasses_.IS_DISABLED);
} else {
label.classList.remove(this.CssClasses_.IS_DISABLED);
}
if (button.checked) {
label.classList.add(this.CssClasses_.IS_CHECKED);
} else {
label.classList.remove(this.CssClasses_.IS_CHECKED);
}
};
/**
* Add blur.
* @private
*/
MaterialSwitch.prototype.blur_ = function(event) {
'use strict';
// TODO: figure out why there's a focus event being fired after our blur,
// so that we can avoid this hack.
window.setTimeout(function() {
this.btnElement_.blur();
}.bind(this), this.Constant_.TINY_TIMEOUT);
};
/**
* Initialize element.
*/
MaterialSwitch.prototype.init = function() {
'use strict';
if (this.element_) {
this.btnElement_ = this.element_.querySelector('.' +
this.CssClasses_.WSK_SWITCH_INPUT);
var track = document.createElement('div');
track.classList.add(this.CssClasses_.WSK_SWITCH_TRACK);
var thumb = document.createElement('div');
thumb.classList.add(this.CssClasses_.WSK_SWITCH_THUMB);
var focusHelper = document.createElement('span');
focusHelper.classList.add(this.CssClasses_.WSK_SWITCH_FOCUS_HELPER);
thumb.appendChild(focusHelper);
this.element_.appendChild(track);
this.element_.appendChild(thumb);
var rippleContainer;
if (this.element_.classList.contains(
this.CssClasses_.WSK_JS_RIPPLE_EFFECT)) {
this.element_.classList.add(
this.CssClasses_.WSK_JS_RIPPLE_EFFECT_IGNORE_EVENTS);
rippleContainer = document.createElement('span');
rippleContainer.classList.add(
this.CssClasses_.WSK_SWITCH_RIPPLE_CONTAINER);
rippleContainer.classList.add(this.CssClasses_.WSK_JS_RIPPLE_EFFECT);
rippleContainer.classList.add(this.CssClasses_.WSK_RIPPLE_CENTER);
var ripple = document.createElement('span');
ripple.classList.add(this.CssClasses_.WSK_RIPPLE);
rippleContainer.appendChild(ripple);
this.element_.appendChild(rippleContainer);
}
this.btnElement_.addEventListener('change', this.onChange_.bind(this));
this.btnElement_.addEventListener('focus', this.onFocus_.bind(this));
this.btnElement_.addEventListener('blur', this.onBlur_.bind(this));
this.element_.addEventListener('mouseup', this.onMouseUp_.bind(this));
rippleContainer.addEventListener('mouseup', this.onMouseUp_.bind(this));
this.updateClasses_(this.btnElement_, this.element_);
this.element_.classList.add('is-upgraded');
}
};
window.addEventListener('load', function() {
'use strict';
// On document ready, the component registers itself. It can assume
// componentHandler is available in the global scope.
componentHandler.register({
constructor: MaterialSwitch,
classAsString: 'MaterialSwitch',
cssClass: 'wsk-js-switch'
});
});