Bug #137 Refactorización Textfield
This commit is contained in:
parent
526929cd56
commit
2594f170cf
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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: '<?'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue