Merge pull request #566 from google/bem-textfield

BEM Update to Textfield component
master
Addy Osmani 2014-12-11 16:19:03 +00:00
commit f3508e74a6
5 changed files with 173 additions and 171 deletions

View File

@ -2,46 +2,58 @@
@import "../animation/animation"; @import "../animation/animation";
@import "../ripple/ripple"; @import "../ripple/ripple";
$textfieldBackgroundColor: transparent; $input-text-font-size: 16px;
$textfieldLabelColor: rgba(0, 0, 0, 0.26); $input-text-width: 100%;
$textfieldBottomBorderColor: rgba(0, 0, 0, 0.12); $input-text-padding: 4px;
$textfieldHighlightColor: nth($primaryPalette, 6); $input-text-ripple-size: 32px;
$textfieldErrorColor: nth($paletteRed, 7);
$textfieldDisabledColor: $textfieldBottomBorderColor;
$textfieldFontSize: 16px; $input-text-background-color: transparent;
$textfieldWidth: 100%; $input-text-label-color: rgba(0, 0, 0, 0.26);
$input-text-bottom-border-color: rgba(0, 0, 0, 0.12);
$input-text-highlight-color: nth($primaryPalette, 6);
$input-text-disabled-color: $input-text-bottom-border-color;
$input-text-error-color: nth($paletteRed, 7);
$inputPadding: 4px; $input-text-floating-label-fontsize: 12px;
$floatingLabelFontSize: 12px; $input-text-expandable-icon-dim: 24px;
$input-text-expandable-search-icon-color: rgba(0, 0, 0, 0.45);
$input-text-expandable-search-icon-highlight-color: $input-text-highlight-color;
$expandableIconDim: 24px; .wsk-input {
$expandableSearchIconColor: rgba(0, 0, 0, 0.45); margin : 20px 0;
$expandableSearchIconHighlightColor: $textfieldHighlightColor; position: relative;
width : 300px;
}
$rippleSize: 32px; .wsk-input--right {
text-align: right;
}
.TextField { .wsk-input--large {
width: 100%;
}
.wsk-textfield {
border : none; border : none;
border-bottom: 1px solid $textfieldBottomBorderColor; border-bottom: 1px solid $input-text-bottom-border-color;
display : block; display : block;
font-size : $textfieldFontSize; font-size : $input-text-font-size;
margin : 0; margin : 0;
padding : $inputPadding 0; padding : $input-text-padding 0;
width : $textfieldWidth; width : $input-text-width;
background : $textfieldBackgroundColor; background : $input-text-font-size;
text-align : left; text-align : left;
} }
.TextField ~ label { .wsk-textfield ~ .wsk-label {
bottom : 0; bottom : 0;
color : $textfieldLabelColor; color : $input-text-label-color;
font-size : $textfieldFontSize; font-size : $input-text-font-size;
left : 0; left : 0;
right : 0; right : 0;
pointer-events: none; pointer-events: none;
position : absolute; position : absolute;
top : $inputPadding; top : $input-text-padding;
width : 100%; width : 100%;
overflow : hidden; overflow : hidden;
white-space : nowrap; white-space : nowrap;
@ -49,8 +61,8 @@ $rippleSize: 32px;
} }
/** The after label is the colored underline for the TextField **/ /** The after label is the colored underline for the TextField **/
.TextField ~ label:after { .wsk-textfield ~ .wsk-label:after {
background-color : $textfieldHighlightColor; background-color : $input-text-highlight-color;
bottom : 0; bottom : 0;
content : ''; content : '';
height : 2px; height : 2px;
@ -62,147 +74,148 @@ $rippleSize: 32px;
} }
/** TextField Focus Styles **/ /** TextField Focus Styles **/
.TextField:focus { .wsk-textfield:focus {
outline: none; outline: none;
} }
.TextField:focus ~ label:after { .wsk-textfield:focus ~ .wsk-label:after {
left : 0; left : 0;
visibility: visible; visibility: visible;
width : 100%; width : 100%;
} }
/** TextField Invalid Styles **/ /** TextField Invalid Styles **/
.TextField:invalid { .wsk-textfield:invalid {
border-color: $textfieldErrorColor; border-color: $input-text-error-color;
box-shadow : none; box-shadow : none;
} }
.TextField:invalid ~label:after { .wsk-textfield:invalid ~ .wsk-label:after {
background-color: $textfieldErrorColor; background-color: $input-text-error-color;
} }
/** TextField Error **/ /** TextField Error **/
.TextField ~ .error { .wsk-textfield ~ .wsk-input__error {
color : $textfieldErrorColor; color : $input-text-error-color;
position : absolute; position : absolute;
font-size : 12px; font-size : 12px;
margin-top: 3px; margin-top: 3px;
visibility: hidden; visibility: hidden;
} }
.TextField:invalid ~ .error { .wsk-textfield:invalid ~ .wsk-input__error {
visibility: visible; visibility: visible;
} }
.TextField.dirty ~ label { .wsk-textfield.is-dirty ~ .wsk-label {
visibility: hidden; visibility: hidden;
} }
/** Floating Label */ /** Floating Label */
.TextField-floatingLabel ~label { .wsk-textfield--floating-label ~ .wsk-label {
@include material-animation-default(); @include material-animation-default();
} }
.TextField-floatingLabel:focus ~ label, .wsk-textfield--floating-label:focus ~ .wsk-label,
.TextField-floatingLabel.dirty ~ label { .wsk-textfield--floating-label.is-dirty ~ .wsk-label {
color : $textfieldHighlightColor; color : $input-text-highlight-color;
font-size : $floatingLabelFontSize; font-size : $input-text-floating-label-fontsize;
top : -($floatingLabelFontSize+$inputPadding); top : -($input-text-floating-label-fontsize+$input-text-padding);
visibility: visible; visibility: visible;
} }
.TextField-floatingLabel:invalid ~ label { .wsk-textfield--floating-label:invalid ~ .wsk-label {
color : $textfieldErrorColor; color : $input-text-error-color;
font-size: 12px; font-size: 12px;
} }
/** Expandable Icon/Holder */ /** Expandable Icon/Holder */
.ExpandableHolder { .wsk-input__expandable-holder{
display : inline-block; display : inline-block;
position : relative; position : relative;
.TextField {
@include material-animation-default();
display : inline-block;
// Safari (possibly others) need to be convinced that this field is actually
// visible, otherwise it cannot be tabbed to nor focused via a <label>.
// TODO: In some cases (Retina displays), this is big enough to render the
// inner element :(
max-width: 0.1px;
&:focus,
&.dirty {
// This is an unforunate hack. Animating between widths in percent (%)
// in many browsers (Chrome, Firefox) only animates the inner visual style
// of the input - the outer bounding box still 'jumps'.
// Thus assume a sensible maximum, and animate to/from that value.
max-width: 600px;
}
}
} }
.ExpandableIcon { .wsk-input__expandable-holder .wsk-textfield {
@include material-animation-default();
display : inline-block;
// Safari (possibly others) need to be convinced that this field is actually
// visible, otherwise it cannot be tabbed to nor focused via a <label>.
// TODO: In some cases (Retina displays), this is big enough to render the
// inner element :(
max-width: 0.1px;
}
.wsk-input__expandable-holder .wsk-textfield:focus,
.wsk-input__expandable-holder .wsk-textfield .is-dirty {
// This is an unforunate hack. Animating between widths in percent (%)
// in many browsers (Chrome, Firefox) only animates the inner visual style
// of the input - the outer bounding box still 'jumps'.
// Thus assume a sensible maximum, and animate to/from that value.
max-width: 600px;
}
.wsk-textfield-expandable-icon {
display : inline-block; display : inline-block;
width : $expandableIconDim; width : $input-text-expandable-icon-dim;
height : $expandableIconDim; height : $input-text-expandable-icon-dim;
text-align : center; text-align : center;
border-color: $expandableSearchIconColor; // inherits to .IconSearch border-color: $input-text-expandable-search-icon-color; // inherits to .wsk-textfield-expandable-icon-search
position : relative; position : relative;
&:hover {
border-color: $expandableSearchIconHighlightColor;
cursor : pointer;
}
.IconSearch {
border : 2px solid #000;
border-color : inherit;
border-radius : 100px;
position : relative;
box-sizing : border-box;
width : 10px;
height : 10px;
display : inline-block;
margin : 1px 4px 4px 1px;
vertical-align: middle;
}
.IconSearch::after {
content : '';
transform : rotate(-45deg);
width : 0;
border-left : 2px solid #000;
border-color: inherit;
height : 5px;
position : absolute;
display : inline-block;
bottom : -5px;
right : -3px;
}
} }
.ExpandableIcon-rippleContainer { .wsk-textfield-expandable-icon:hover {
border-color: $input-text-expandable-search-icon-highlight-color;
cursor : pointer;
}
.wsk-textfield-expandable-icon .wsk-textfield-expandable-icon-search {
border : 2px solid #000;
border-color : inherit;
border-radius : 100px;
position : relative;
box-sizing : border-box;
width : 10px;
height : 10px;
display : inline-block;
margin : 1px 4px 4px 1px;
vertical-align: middle;
}
.wsk-textfield-expandable-icon .wsk-textfield-expandable-icon-search::after {
content : '';
transform : rotate(-45deg);
width : 0;
border-left : 2px solid #000;
border-color: inherit;
height : 5px;
position : absolute;
display : inline-block;
bottom : -5px;
right : -3px;
}
.wsk-textfield-expandable-icon__ripple__container {
position: absolute; position: absolute;
z-index: 2; z-index: 2;
top: -((($rippleSize - $expandableIconDim) / 2) - 0px); top: -((($input-text-ripple-size - $input-text-expandable-icon-dim) / 2) - 0px);
left: -(($rippleSize - $expandableIconDim) / 2); left: -(($input-text-ripple-size - $input-text-expandable-icon-dim) / 2);
box-sizing: border-box; box-sizing: border-box;
width: $rippleSize; width: $input-text-ripple-size;
height: $rippleSize; height: $input-text-ripple-size;
border-radius: 50%; border-radius: 50%;
overflow: hidden; overflow: hidden;
-webkit-mask-image: -webkit-radial-gradient(circle, white, black); -webkit-mask-image: -webkit-radial-gradient(circle, white, black);
} }
.ExpandableIcon-rippleContainer .Ripple { .wsk-textfield-expandable-icon__ripple__container .Ripple {
background: $expandableSearchIconHighlightColor; background: $input-text-expandable-search-icon-highlight-color;
} }
/** Disabled Styling **/ /** Disabled Styling **/
.TextField[disabled] { .wsk-textfield[disabled] {
background-color: transparent; background-color: transparent;
border-bottom : 1px dashed $textfieldDisabledColor; border-bottom : 1px dashed $input-text-disabled-color;
} }

View File

@ -5,7 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Textfield</title> <title>wsk-textfield</title>
<link rel="stylesheet" href="demo.css"> <link rel="stylesheet" href="demo.css">
@ -16,80 +16,80 @@
<div class="preview-block"> <div class="preview-block">
<form action="#"> <form action="#">
<div class="input-container"> <div class="wsk-input">
<input class="TextField" type="text" name="sample" /> <input class="wsk-textfield wsk-js-textfield" type="text" name="sample" />
<label for="sample">Type Something...</label> <label class="wsk-label" for="sample">Type Something...</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<input class="TextField" type="text" pattern="[0-9]*" name="sample" /> <input class="wsk-textfield wsk-js-textfield" type="text" pattern="[0-9]*" name="sample" />
<label for="sample">Numbers Only</label> <label class="wsk-label" for="sample">Numbers Only</label>
<span class="error">Input is not a number!</span> <span class="wsk-input__error">Input is not a number!</span>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<input class="TextField" type="text" disabled name="sample" /> <input class="wsk-textfield wsk-js-textfield" type="text" disabled name="sample" />
<label for="sample">I'm Disabled</label> <label class="wsk-label" class="wsk-label" for="sample">I'm Disabled</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<input class="TextField TextField-floatingLabel" type="text" name="sample" /> <input class="wsk-textfield wsk-js-textfield wsk-textfield--floating-label" type="text" name="sample" />
<label for="sample">Text Input</label> <label for="sample">Text Input</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<input class="TextField TextField-floatingLabel" type="text" pattern="[0-9]*" name="sample" /> <input class="wsk-textfield wsk-js-textfield wsk-textfield--floating-label" type="text" pattern="[0-9]*" name="sample" />
<label for="sample">Numbers Only</label> <label class="wsk-label" for="sample">Numbers Only</label>
<span class="error">Input is not a number!</span> <span class="wsk-input__error">Input is not a number!</span>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<textarea class="TextField" type="text" rows= "1" name="sample" ></textarea> <textarea class="wsk-textfield wsk-js-textfield" type="text" rows= "1" name="sample" ></textarea>
<label for="sample">Type multiple lines here...</label> <label class="wsk-label" for="sample">Type multiple lines here...</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<textarea class="TextField" type="text" rows="3" name="sample" ></textarea> <textarea class="wsk-textfield wsk-js-textfield" type="text" rows="3" name="sample" ></textarea>
<label for="sample">This input is 3 rows high</label> <label class="wsk-label" for="sample">This input is 3 rows high</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<textarea class="TextField" type="text" maxrows="3" rows="1" name="sample" ></textarea> <textarea class="wsk-textfield wsk-js-textfield" type="text" maxrows="3" rows="1" name="sample" ></textarea>
<label for="sample">This input is at most 3 rows high</label> <label class="wsk-label" for="sample">This input is at most 3 rows high</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<textarea class="TextField TextField-floatingLabel" type="text" rows= "1" name="sample" ></textarea> <textarea class="wsk-textfield wsk-js-textfield wsk-textfield--floating-label" type="text" rows= "1" name="sample" ></textarea>
<label for="sample">Multiple lines and a floating label</label> <label class="wsk-label" for="sample">Multiple lines and a floating label</label>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<label class="ExpandableIcon" for="sample-expandable"> <label class="wsk-textfield-expandable-icon wsk-label" for="sample-expandable">
<span class="IconSearch"></span> <span class="wsk-textfield-expandable-icon-search"></span>
</label> </label>
<div class="ExpandableHolder"> <div class="wsk-input__expandable-holder">
<input class="TextField TextField-expandable" type="text" name="sample" id="sample-expandable" /> <input class="wsk-textfield wsk-js-textfield wsk-textfield--expandable" type="text" name="sample" id="sample-expandable" />
<label for="sample">Expandable Input</label> <label class="wsk-label" for="sample">Expandable Input</label>
</div> </div>
</div> </div>
<div class="input-container"> <div class="wsk-input">
<label class="ExpandableIcon" for="sample-expandable-floating"> <label class="wsk-textfield-expandable-icon wsk-label" for="sample-expandable-floating">
<span class="IconSearch"></span> <span class="wsk-textfield-expandable-icon-search"></span>
</label> </label>
<div class="ExpandableHolder"> <div class="wsk-input__expandable-holder">
<input class="TextField TextField-expandable TextField-floatingLabel" type="text" name="sample" id="sample-expandable-floating" /> <input class="wsk-textfield wsk-js-textfield wsk-textfield--expandable wsk-textfield--floating-label" type="text" name="sample" id="sample-expandable-floating" />
<label for="sample">Expandable &amp; Floating Input</label> <label class="wsk-label" for="sample">Expandable &amp; Floating Input</label>
</div> </div>
</div> </div>
<div class="input-container right large"> <div class="wsk-input wsk-input--right wsk-input--large">
Right/No Label: Right/No Label:
<label class="ExpandableIcon" for="sample-expclean"> <label class="wsk-textfield-expandable-icon wsk-label" for="sample-expclean">
<span class="IconSearch"></span> <span class="wsk-textfield-expandable-icon-search"></span>
</label> </label>
<div class="ExpandableHolder"> <div class="wsk-input__expandable-holder">
<input class="TextField TextField-expandable" type="text" name="sample" id="sample-expclean" /> <input class="wsk-textfield wsk-js-textfield wsk-textfield--expandable" type="text" name="sample" id="sample-expclean" />
</div> </div>
</div> </div>
</form> </form>

View File

@ -1,18 +1,7 @@
@import "../styleguide_demo_bp"; @import "../styleguide_demo_bp";
@import "_textfield"; @import "_textfield";
form div.input-container {
margin : 20px 0;
position: relative;
width : 300px;
&.right {
text-align: right;
}
&.large {
width: 100%;
}
}
textarea { textarea {
overflow: auto; overflow: auto;

View File

@ -17,9 +17,9 @@ function TextField(element) {
this.onInputChange = function(evt) { this.onInputChange = function(evt) {
if (evt.target.value && evt.target.value.length > 0) { if (evt.target.value && evt.target.value.length > 0) {
evt.target.classList.add('dirty'); evt.target.classList.add('is-dirty');
} else { } else {
evt.target.classList.remove('dirty'); evt.target.classList.remove('is-dirty');
} }
}; };
@ -44,7 +44,7 @@ function ExpandableIcon(iconElement) {
var container = document.createElement('span'); var container = document.createElement('span');
iconElement.appendChild(container); iconElement.appendChild(container);
container.classList.add('ExpandableIcon-rippleContainer'); container.classList.add('input__expandable__icon__ripple__container');
container.classList.add('RippleEffect'); container.classList.add('RippleEffect');
container.classList.add('RippleEffect--recentering'); container.classList.add('RippleEffect--recentering');
@ -55,12 +55,12 @@ function ExpandableIcon(iconElement) {
window.addEventListener('load', function() { window.addEventListener('load', function() {
var i; var i;
var inputs = document.querySelectorAll('.TextField'); var inputs = document.querySelectorAll('.wsk-js-textfield');
for (i = 0; i < inputs.length; i++) { for (i = 0; i < inputs.length; i++) {
var input = inputs[i]; var input = inputs[i];
new TextField(input); new TextField(input);
} }
var expandableIcons = document.querySelectorAll('.ExpandableIcon'); var expandableIcons = document.querySelectorAll('.wsk-textfield-expandable-icon');
for (i = 0; i < expandableIcons.length; ++i) { for (i = 0; i < expandableIcons.length; ++i) {
var expandableIcon = expandableIcons[i]; var expandableIcon = expandableIcons[i];
new ExpandableIcon(expandableIcon); new ExpandableIcon(expandableIcon);

View File

@ -169,7 +169,7 @@ gulp.task('serve', ['styles'], function() {
gulp.watch(['app/**/**/**/*.html'], reload); gulp.watch(['app/**/**/**/*.html'], reload);
gulp.watch(['app/**/**/**/*.{scss,css}'], ['styles', reload]); gulp.watch(['app/**/**/**/*.{scss,css}'], ['styles', reload]);
gulp.watch(['app/scripts/**/*.js'], ['jshint']); gulp.watch(['app/scripts/**/*.js','app/styleguide/**/*.js'], ['jshint']);
gulp.watch(['app/images/**/*'], reload); gulp.watch(['app/images/**/*'], reload);
}); });