Bug #137 Refactorización Textfield

This commit is contained in:
gerard 2018-06-28 15:54:54 +02:00
parent 526929cd56
commit 2594f170cf
4 changed files with 227 additions and 87 deletions

View File

@ -1,45 +1,124 @@
@import "colors";
vn-textfield { vn-textfield {
.mdl-chip__action { margin: 20px 0!important;
position: absolute; display: block;
width: auto;
top: 0px; .leftIcons, .rightIcons, .suffix{
right: -6px; display: inline-flex;
margin: 21px 0px; color: $secondary-font-color;
background: white; & .material-icons{
opacity: 1; font-size: 20px!important
z-index: 1;
color: #aaa;
}
.mdl-textfield {
width: 100%;
}
.mdl-textfield__error {
visibility: visible;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
}
.mdl-textfield.invalid {
.mdl-textfield__input {
border-color: #d50000;
box-shadow: none;
} }
.mdl-textfield__label::after { }
.leftIcons{
margin-right: 3px;
}
.container{
width: 100%;
position: relative;
padding-bottom: 2px;
}
.textField{
width: 100%;
display: inline-flex;
position: relative;
padding: 4px 0;
}
.infix {
position: relative;
display: block;
flex: auto;
width: 100%;
min-width: 0;
}
i.pointer {
visibility: hidden;
}
i.visible {
visibility: visible;
}
label {
position: absolute;
bottom: 2px;
pointer-events: none;
color: $secondary-font-color;
transition-duration: .2s;
transition-timing-function: cubic-bezier(.4,0,.2,1);
}
input {
outline: none;
border: none;
font-family: "Helvetica","Arial",sans-serif;
display: block;
font-size: 16px;
width: 100%;
background: 0 0;
color: inherit;
&[type=number] {
-moz-appearance: textfield;
&::-webkit-outer-spin-button,
&::-webkit-inner-spin-button{
-webkit-appearance: none;
margin: 0;
}
}
&:invalid {
box-shadow:none;
}
}
.underline{
position: absolute;
bottom: 0;
height: 1px;
content: ' ';
pointer-events: none;
width: 100%;
background-color: rgba(0,0,0,.12);
}
.selected.underline{
background-color: rgb(255,152,0);
height: 2px;
left: 50%;
width: 0px!important;
transition-duration: 0.2s;
transition-timing-function: cubic-bezier(.4,0,.2,1);
}
&.not-empty {
& label {
bottom: 24px;
color: $main-01;
font-size: 12px;
}
}
div.selected{
&.container{
border-bottom: 0px;
}
& label {
bottom: 24px;
color: $main-01;
font-size: 12px;
}
& .selected.underline{
left: 0;
width: 100%!important;
}
}
& > div.container > div.textField > div.infix.invalid{
@extend div.selected;
& > span.mdl-textfield__error{
visibility: visible;
margin-top: 9px;
}
& > label{
color: #d50000;
}
}
.infix.invalid + .underline {
&{
background-color: #d50000; background-color: #d50000;
} }
} }
.mdl-textfield--floating-label.invalid .mdl-textfield__label {
color: #d50000;
font-size: 12px;
top: 4px;
}
.material-icons {
font-size: 18px;
float: right;
margin-right: 5px;
}
.material-icons:hover {
color: rgba(0,0,0, .87);
}
} }

View File

@ -1,29 +1,46 @@
<div <div class="container"
class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label" ng-class="{selected: $ctrl.hasFocus}"
ng-mouseenter="$ctrl.hasMouseIn = true" ng-mouseenter="$ctrl.hasMouseIn = true"
ng-mouseleave="$ctrl.hasMouseIn = false"> ng-mouseleave="$ctrl.hasMouseIn = false"
<input style="display: inline-flex"
class="mdl-textfield__input" >
type="{{$ctrl.type}}" <div class="textField">
name="{{$ctrl.name}}" <div class="leftIcons">
ng-model="$ctrl.value" </div>
vn-validation="{{$ctrl.rule}}"
ng-disabled="$ctrl.disabled" <div class="infix">
ng-readonly="$ctrl.readonly" <input
ng-focus="$ctrl.hasFocus = true" type="{{$ctrl.type}}"
ng-blur="$ctrl.hasFocus = false" name="{{$ctrl.name}}"
tabindex="{{$ctrl.input.tabindex}}"/> ng-model="$ctrl.value"
<div class="mdl-chip__action"> vn-validation="{{$ctrl.rule}}"
<i class="material-icons" ng-disabled="$ctrl.disabled"
ng-if="$ctrl.hasInfo" ng-readonly="$ctrl.readonly"
vn-tooltip="{{$ctrl.info}}"> ng-focus="$ctrl.hasFocus = true"
info_outline ng-blur="$ctrl.hasFocus = false"
</i> tabindex="{{$ctrl.input.tabindex}}"
<i class="material-icons pointer" />
ng-show="!$ctrl.disabled && $ctrl.hasValue && ($ctrl.hasFocus || $ctrl.hasMouseIn)"
ng-click="$ctrl.clear()"> <label class="label" translate>{{::$ctrl.label}}</label>
clear </div>
</i> <div class="underline"></div>
<div class="selected underline"></div>
<div class="suffix">
<i class="material-icons pointer"
ng-class="{visible:
!$ctrl.disabled
&& $ctrl.hasValue
&& ($ctrl.hasFocus || $ctrl.hasMouseIn)
&& !$ctrl.unclearable}"
ng-click="$ctrl.clear()">
clear
</i>
<i class="material-icons"
ng-if="$ctrl.hasInfo"
vn-tooltip="{{$ctrl.info}}">
info_outline
</i>
</div>
<div class="rightIcons"></div>
</div> </div>
<label class="mdl-textfield__label" translate>{{::$ctrl.label}}</label>
</div> </div>

View File

@ -3,45 +3,73 @@ import Input from '../../lib/input';
import './style.scss'; import './style.scss';
export default class Textfield extends Input { export default class Textfield extends Input {
constructor($element, $scope, $attrs, vnTemplate) { constructor($element, $scope, $attrs, vnTemplate, $transclude) {
super($element, $scope); super($element, $scope);
vnTemplate.normalizeInputAttrs($attrs); vnTemplate.normalizeInputAttrs($attrs);
this._value = null; this._value = null;
this.type = $attrs.type || 'text'; this.type = $attrs.type;
this.showActions = false; this.showActions = false;
this.hasInfo = Boolean($attrs.info); this.hasInfo = Boolean($attrs.info);
this.info = $attrs.info || null; this.info = $attrs.info || null;
this.hasFocus = false; this.hasFocus = false;
this.hasMouseIn = false; this.hasMouseIn = false;
componentHandler.upgradeElement($element[0].firstChild);
if ($transclude) {
$transclude($scope.$parent, tClone => {
this.leftIcons = tClone[0];
}, null, 'leftIcons');
$transclude($scope.$parent, tClone => {
this.rightIcons = tClone[0];
}, null, 'rightIcons');
}
} }
get value() { set onChange(value) {
return this._value; this.input.addEventListener('change', value);
}
set leftIcons(value) {
for (let i = 0; i < value.children.length; i++) {
this.element.querySelector('.leftIcons').appendChild(value.children[i]);
}
}
set rightIcons(value) {
for (let i = 0; i < value.children.length; i++) {
this.element.querySelector('.rightIcons').appendChild(value.children[i]);
}
} }
set value(value) { set value(value) {
this._value = (value === undefined || value === '') ? null : value; this._value = (value === undefined || value === '') ? null : value;
this.input.value = this._value; this.input.value = this._value;
this.hasValue = Boolean(this._value); this.hasValue = this._value !== null;
this.mdlUpdate();
}
if (this.hasValue) this.element.classList.add('not-empty');
else this.element.classList.remove('not-empty');
}
get value() {
return this._value;
}
set type(value) {
this._type = value || 'text';
}
get type() {
return this._type;
}
set vnTabIndex(value) { set vnTabIndex(value) {
this.input.tabindex = value; this.input.tabindex = value;
} }
clear() { clear() {
this.value = null; this.value = null;
this.input.focus(); this.input.focus();
} }
mdlUpdate() {
let mdlElement = this.element.firstChild.MaterialTextfield;
if (mdlElement) mdlElement.updateClasses_();
}
} }
Textfield.$inject = ['$element', '$scope', '$attrs', 'vnTemplate']; Textfield.$inject = ['$element', '$scope', '$attrs', 'vnTemplate', '$transclude'];
ngModule.component('vnTextfield', { ngModule.component('vnTextfield', {
template: require('./textfield.html'), template: require('./textfield.html'),
transclude: {
leftIcons: '?tLeftIcons',
rightIcons: '?tRightIcons'
},
controller: Textfield, controller: Textfield,
bindings: { bindings: {
value: '=model', value: '=model',
@ -51,6 +79,8 @@ ngModule.component('vnTextfield', {
readonly: '<?', readonly: '<?',
rule: '@?', rule: '@?',
type: '@?', type: '@?',
vnTabIndex: '@?' vnTabIndex: '@?',
onChange: '&',
unclearable: '<?'
} }
}); });

View File

@ -18,30 +18,44 @@ describe('Component vnTextfield', () => {
$attrs = {}; $attrs = {};
$timeout = _$timeout_; $timeout = _$timeout_;
$element = angular.element('<div><input></div>'); $element = angular.element('<div><input></div>');
controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout}); controller = $componentController('vnTextfield', {$scope, $element, $attrs, $timeout, $transclude: null});
})); }));
describe('value() setter', () => { describe('value() setter', () => {
it(`should set _value, input.value and hasValue properties to null, '' and false then call mdlUpdate()`, () => { it(`should set _value, input.value and hasValue properties to null, '' and false`, () => {
spyOn(controller, 'mdlUpdate');
let testValue = ''; let testValue = '';
controller.value = testValue; controller.value = testValue;
expect(controller._value).toEqual(null); expect(controller._value).toEqual(null);
expect(controller.input.value).toEqual(testValue); expect(controller.input.value).toEqual(testValue);
expect(controller.hasValue).toEqual(Boolean(testValue)); expect(controller.hasValue).toEqual(Boolean(testValue));
expect(controller.mdlUpdate).toHaveBeenCalledWith();
}); });
it(`should set _value, input.value and hasValue propertiest to test, test and true then call mdlUpdate()`, () => { it(`should set _value, input.value and hasValue propertiest to test, test and true`, () => {
spyOn(controller, 'mdlUpdate');
let testValue = 'test'; let testValue = 'test';
controller.value = testValue; controller.value = testValue;
expect(controller._value).toEqual(testValue); expect(controller._value).toEqual(testValue);
expect(controller.input.value).toEqual(testValue); expect(controller.input.value).toEqual(testValue);
expect(controller.hasValue).toEqual(Boolean(testValue)); expect(controller.hasValue).toEqual(Boolean(testValue));
expect(controller.mdlUpdate).toHaveBeenCalledWith(); });
});
describe('type() setter', () => {
it(`should set _type to 'text' if theres not given value`, () => {
controller.type = null;
expect(controller._type).toEqual('text');
});
});
describe('clear()', () => {
it(`should set value property to null and call focus`, () => {
spyOn(controller.input, 'focus');
controller.clear();
expect(controller.value).toEqual(null);
expect(controller.input.focus).toHaveBeenCalledWith();
}); });
}); });
}); });