material-input
Advanced tools
Comparing version 0.0.2 to 0.0.3
'use strict'; | ||
// define attributes that are not supposed to be transferred | ||
@@ -11,2 +12,4 @@ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
var attributesExceptions = ['name', 'label', 'tabindex', 'placeholder', 'autofocus', 'autocomplete', 'autovalidate']; | ||
var MaterialInput = function (_HTMLElement) { | ||
@@ -24,14 +27,26 @@ _inherits(MaterialInput, _HTMLElement); | ||
value: function createdCallback() { | ||
this.createShadowRoot().innerHTML = '\n <style>\n :host{\n color: rgb(54,79,199);\n display: block;\n position: relative;\n }\n .material-input__container{\n width: inherit;\n display: block;\n position: relative;\n margin: .5em 0;\n }\n .material-input__input{\n box-sizing: border-box;\n position: relative;\n background-color: transparent;\n font-size: 1em;\n color: black;\n padding: 1em 1em 1em 10px;\n display: block;\n width: 100%;\n border: none;\n border-bottom: .1rem solid rgb(206,212,218);\n }\n .material-input__input:focus{\n outline: none;\n color: black;\n }\n /* placeholder and placeholder fade on focus */\n .material-input__input::-webkit-input-placeholder {\n color: rgb(134,142,150);\n opacity: 1;\n }\n .material-input__input:focus::-webkit-input-placeholder {\n opacity: .5;\n transition: opacity .35s ease;\n }\n .material-input__input::-moz-placeholder {\n color: rgb(134,142,150);\n opacity: 1;\n }\n .material-input__input:focus::-moz-placeholder {\n opacity: .5;\n transition: opacity .35s ease;\n }\n .material-input__input:-ms-input-placeholder {\n color: rgb(134,142,150);\n opacity: 1;\n }\n .material-input__input:-ms-input-placeholder {\n opacity: .5;\n transition: opacity .35s ease;\n }\n /* Labels */\n .material-input__label{\n color: rgb(134,142,150);\n font-size: inherit;\n pointer-events: none;\n position: absolute;\n left: 10px;\n bottom: 1em;\n transition: 0.2s ease all;\n }\n .material-input__container.is-empty .material-input__input[placeholder] ~ .material-input__label{\n color: black;\n }\n /* active state */\n .material-input__input:focus ~ .material-input__label,\n .material-input__container:not(.is-empty) .material-input__label,\n .material-input__container.label-always-floats .material-input__label{\n bottom: 3em;\n font-size: .75em;\n }\n .material-input__input:focus ~ .material-input__label,\n .material-input__container.is-empty .material-input__input[placeholder]:focus ~ .material-input__label{\n color: inherit;\n }\n .material-input__bar{\n position:relative;\n display:block;\n width:100%;\n }\n .material-input__bar::before, .material-input__bar::after {\n content:\'\';\n height:2px;\n width:0;\n bottom:0;\n position:absolute;\n background: currentColor;\n transition:0.2s ease all;\n }\n .material-input__bar::before {\n left:50%;\n }\n .material-input__bar::after {\n right:50%;\n }\n .material-input__input:focus ~ .material-input__bar:before, .material-input__input:focus ~ .material-input__bar:after{\n width:50%;\n }\n </style>\n <div class="material-input__container">\n <input class="material-input__input" tabindex="-1" />\n <label class="material-input__label"></label>\n <div class="material-input__bar"></div>\n <div class="material-input__message"></div>\n </div>\n '; | ||
// initialize attributes | ||
var value = ''; | ||
// set value of material-input | ||
Object.defineProperty(this, "value", { | ||
configurable: true, | ||
enumerable: true, | ||
get: function get() { | ||
return value; | ||
}, | ||
set: function set(newValue) { | ||
value = !newValue ? '' : newValue; | ||
this._value(value); | ||
} | ||
}); | ||
this.createShadowRoot().innerHTML = '\n <style>\n :host{\n display: block;\n position: relative;\n background: transparent;\n margin: .5em 0;\n }\n .material-input__container{\n width: inherit;\n display: block;\n position: relative;\n }\n .material-input__input{\n box-sizing: border-box;\n position: relative;\n background-color: transparent;\n font-size: 1em;\n color: var(--material-input-text-color, black);\n padding: 1em 1em 1em 10px;\n display: block;\n width: 100%;\n border: none;\n border-bottom: var(--material-input-line-height, 1px) solid var(--material-input-border-color, rgb(206,212,218));\n box-shadow: none;\n }\n .material-input__container.invalid .material-input__input{\n border-bottom-color: var(--material-input-invalid-color, rgb(224,49,49));\n }\n .material-input__container.valid .material-input__input{\n border-bottom-color: var(--material-input-valid-color, rgb(47,158,68));\n }\n .material-input__input:focus{\n outline: none;\n }\n /* placeholder and placeholder fade on focus */\n .material-input__input::-webkit-input-placeholder {\n color: var(--material-input-placeholder-color, rgb(134,142,150));\n opacity: 1;\n }\n .material-input__input:focus::-webkit-input-placeholder {\n opacity: .5;\n transition: opacity .35s ease;\n }\n .material-input__input::-moz-placeholder {\n color: var(--material-input-placeholder-color, rgb(134,142,150));\n opacity: 1;\n }\n .material-input__input:focus::-moz-placeholder {\n opacity: .5;\n transition: opacity .35s ease;\n }\n .material-input__input:-ms-input-placeholder {\n color: var(--material-input-placeholder-color, rgb(134,142,150));\n opacity: 1;\n }\n .material-input__input:-ms-input-placeholder {\n opacity: .5;\n transition: opacity .35s ease;\n }\n /* Labels */\n .material-input__label{\n color: rgb(134,142,150);\n font-size: inherit;\n pointer-events: none;\n position: absolute;\n left: 10px;\n bottom: 1em;\n transition: 0.2s ease all;\n }\n .material-input__container.no-animation .material-input__label,\n .material-input__container.label-always-floats .material-input__label{\n transition: 0s ease all;\n }\n .material-input__container.is-empty .material-input__input[placeholder] ~ .material-input__label{\n color: var(--material-input-text-color, black);\n }\n /* active state */\n .material-input__input:focus ~ .material-input__label,\n .material-input__container:not(.is-empty) .material-input__label,\n .material-input__container.label-always-floats .material-input__label{\n bottom: 3em;\n font-size: .75em;\n }\n .material-input__input:focus ~ .material-input__label,\n .material-input__container.is-empty .material-input__input[placeholder]:focus ~ .material-input__label{\n color: var(--material-input-highlight-color, rgb(54,79,199));\n }\n /* errror state */\n .material-input__container.invalid.label-always-floats .material-input__label,\n .material-input__container.invalid .material-input__input:focus ~ .material-input__label,\n .material-input__container.is-empty.invalid .material-input__input[placeholder]:focus ~ .material-input__label,\n .material-input__container.is-empty.invalid .material-input__input[placeholder] ~ .material-input__label{\n color: var(--material-input-invalid-color, rgb(224,49,49));\n }\n /* valid state */\n .material-input__container.valid.label-always-floats .material-input__label,\n .material-input__container.valid .material-input__input:focus ~ .material-input__label,\n .material-input__container.is-empty.valid .material-input__input[placeholder]:focus ~ .material-input__label,\n .material-input__container.is-empty.valid .material-input__input[placeholder] ~ .material-input__label{\n color: var(--material-input-valid-color, rgb(47,158,68));\n }\n /* bar */\n .material-input__bar{\n position:relative;\n display:block;\n width:100%;\n }\n .material-input__bar::before, .material-input__bar::after {\n content:\'\';\n height: var(--material-input-highlight-line-height, 2px);\n width:0;\n bottom:0;\n position:absolute;\n background: var(--material-input-highlight-color, rgb(54,79,199));\n transition:0.2s ease all;\n }\n .material-input__container.invalid .material-input__bar::before,\n .material-input__container.invalid .material-input__bar::after{\n background: var(--material-input-invalid-color, rgb(224,49,49));\n }\n .material-input__container.valid .material-input__bar::before,\n .material-input__container.valid .material-input__bar::after{\n background: var(--material-input-valid-color, rgb(47,158,68));\n }\n .material-input__bar::before {\n left:50%;\n }\n .material-input__bar::after {\n right:50%;\n }\n .material-input__input:focus ~ .material-input__bar:before, .material-input__input:focus ~ .material-input__bar:after{\n width:50%;\n }\n </style>\n <div class="material-input__container no-animation' + (this.value == '' ? ' is-empty' : '') + '">\n <input class="material-input__input" tabindex="-1" />\n <label class="material-input__label"></label>\n <div class="material-input__bar"></div>\n <div class="material-input__message"></div>\n </div>\n '; | ||
// set tab index to make element focussable | ||
this.setAttribute('tabindex', 0); | ||
this.type = this.getAttribute('type') || 'text'; | ||
this.value = this.getAttribute('value') || null; | ||
this.name = this.getAttribute('name') || 'material-input'; | ||
this.label = this.getAttribute('label') || null; | ||
this.placeholder = this.getAttribute('placeholder') || null; | ||
this.invalid = this.hasAttribute('invalid') || false; | ||
this.valid = this.hasAttribute('valid') || false; | ||
// add input field for form submisson | ||
this.insertAdjacentHTML('afterend', '<input tabindex="-1" type="hidden" name="' + this.name + '"/>'); | ||
// shim shadowDOM styling | ||
if (WebComponents !== undefined && WebComponents.flags.shadow === true) { | ||
WebComponents.ShadowCSS.shimStyling(this.shadowRoot, 'material-input'); | ||
} | ||
// add hidden input | ||
this.insertAdjacentHTML('afterend', '<input tabindex="-1" style="pointer-events: none; margin:0; border: 0; height: 0; opacity: 0; position: absolute; top: ' + (this.offsetTop + this.offsetHeight) + 'px; left: ' + this.offsetLeft + 'px;" name="' + this.getAttribute('name') + '"/>'); | ||
this.$hiddenInput = document.querySelector('input[name=' + this.getAttribute('name') + ']'); | ||
// elements | ||
@@ -42,14 +57,28 @@ this.$container = this.shadowRoot.querySelector('.material-input__container'); | ||
this.$message = this.$container.querySelector('.material-input__message'); | ||
this.$hiddenInput = document.querySelector('input[name=' + this.name + ']'); | ||
// add events | ||
this.$hiddenInput.addEventListener('invalid', function () { | ||
this._setValid(false); | ||
}.bind(this)); | ||
this.$input.setAttribute('type', this.type); | ||
this.$input.addEventListener('keydown', function () { | ||
this.$input.addEventListener('blur', function () { | ||
this._value(this.$input.value); | ||
// check if is valid | ||
if (this.$input.value !== '' && (this.$input.validity.valid === true || this.$input.validity.valid === false)) { | ||
this._setValid(this.$input.validity.valid); | ||
} | ||
}.bind(this)); | ||
this.$input.addEventListener('blur', function () { | ||
this.$input.addEventListener('keydown', function () { | ||
this._value(this.$input.value); | ||
}.bind(this)); | ||
if (this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false') { | ||
this.$input.addEventListener('keydown', function () { | ||
// check if is valid | ||
if (this.$input.value !== '' && this.$input.validity.valid === true) { | ||
this._setValid(true); | ||
} | ||
}.bind(this)); | ||
} | ||
this.addEventListener('focus', function () { | ||
@@ -59,23 +88,140 @@ this.$input.focus(); | ||
if (this.value) { | ||
this.$input.value = this.value; | ||
this._transferAttributes(); | ||
this._setValue(this.getAttribute('value')); | ||
this._setLabel(this.getAttribute('label')); | ||
this._setPlaceholder(this.getAttribute('placeholder')); | ||
this._setValid(); | ||
// remove no-animation loading class | ||
setTimeout(function () { | ||
this.$container.classList.remove('no-animation'); | ||
}.bind(this), 100); | ||
} | ||
/** | ||
* when an attribute is changed | ||
*/ | ||
}, { | ||
key: 'attributeChangedCallback', | ||
value: function attributeChangedCallback(attrName, oldVal, newVal) { | ||
// define callbacks | ||
var callbacks = { | ||
'valid': this._setValid, | ||
'value': this._setValue, | ||
'label': this._setLabel, | ||
'placeholder': this._setPlaceholder | ||
}; | ||
// call callback if it exists | ||
if (callbacks.hasOwnProperty(attrName)) { | ||
callbacks[attrName].call(this, newVal, oldVal); | ||
} else { | ||
// if other attributes are updated, transfer updates to hidden input field | ||
this._transferAttribute(attrName, newVal, attributesExceptions); | ||
} | ||
} | ||
/** | ||
* set the custom validity of the input | ||
*/ | ||
this._value(this.value); | ||
this._label(this.label); | ||
this._placeholder(this.placeholder); | ||
}, { | ||
key: 'setCustomValidity', | ||
value: function setCustomValidity(msg) { | ||
this.$input.setCustomValidity(msg); | ||
this.$hiddenInput.setCustomValidity(msg); | ||
} | ||
/** | ||
* set value | ||
*/ | ||
if (this.required) { | ||
this.$hiddenInput.setAttribute('required', ''); | ||
}, { | ||
key: '_setValue', | ||
value: function _setValue(newValue) { | ||
this.value = newValue; | ||
} | ||
/** | ||
* set field to valid or invalid | ||
*/ | ||
}, { | ||
key: '_setValid', | ||
value: function _setValid() { | ||
var validity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined; | ||
// valid is not set | ||
if (!this.hasAttribute('valid')) { | ||
this.valid = undefined; | ||
this.$container.classList.remove('valid'); | ||
this.$container.classList.remove('invalid'); | ||
} | ||
// valid is true | ||
if (this.getAttribute('valid') === 'true' || this.getAttribute('valid') === true || validity === true || validity === 'true') { | ||
this.valid = true; | ||
this.$container.classList.add('valid'); | ||
this.$container.classList.remove('invalid'); | ||
} | ||
// valid is false | ||
if (this.getAttribute('valid') === 'false' || this.getAttribute('valid') === false || validity === false || validity === 'false') { | ||
this.valid = false; | ||
this.$container.classList.add('invalid'); | ||
this.$container.classList.remove('valid'); | ||
} | ||
} | ||
/** | ||
* transfer attributes to input | ||
*/ | ||
}, { | ||
key: '_transferAttributes', | ||
value: function _transferAttributes() { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = Object.keys(this.attributes)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var key = _step.value; | ||
if (this.attributes.hasOwnProperty(key)) { | ||
this._transferAttribute(this.attributes[key].name, this.attributes[key].value, attributesExceptions); | ||
} | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* transfer attribute to input | ||
*/ | ||
}, { | ||
key: '_transferAttribute', | ||
value: function _transferAttribute(attrName, val, attributesExceptions) { | ||
if (attributesExceptions.indexOf(attrName) === -1) { | ||
this.$hiddenInput.setAttribute(attrName, val); | ||
this.$input.setAttribute(attrName, val); | ||
} | ||
} | ||
/** | ||
* update value and toggle is-empty class | ||
*/ | ||
}, { | ||
key: '_value', | ||
value: function _value(value) { | ||
// set value of material-input | ||
this.value = !value ? '' : value; | ||
value: function _value(val) { | ||
// set value of hidden input for form submission | ||
this.$hiddenInput.value = this.value; | ||
this.$hiddenInput.value = val; | ||
this.$input.value = val; | ||
// set state depending on value | ||
this._toggle(this.$container, 'is-empty', this.value === ''); | ||
this._toggle(this.$container, 'is-empty', val === ''); | ||
} | ||
@@ -87,17 +233,24 @@ /** | ||
}, { | ||
key: '_label', | ||
value: function _label(label) { | ||
this.$label.innerHTML = label; | ||
this._toggle(this.$container, 'label-always-floats', this.placeholder !== null); | ||
key: '_setLabel', | ||
value: function _setLabel(label) { | ||
if (label !== undefined && label !== null) { | ||
return this.$label.innerHTML = label; | ||
} | ||
this.$label.innerHTML = ''; | ||
} | ||
/** | ||
* set placeholder and add label-always-floats class | ||
*/ | ||
}, { | ||
key: '_placeholder', | ||
value: function _placeholder(placeholder) { | ||
if (placeholder !== null) { | ||
this.$input.placeholder = placeholder; | ||
} else { | ||
this.$input.removeAttribute('placeholder'); | ||
key: '_setPlaceholder', | ||
value: function _setPlaceholder(placeholder) { | ||
if (placeholder !== null && placeholder !== undefined) { | ||
this.$input.setAttribute('placeholder', placeholder); | ||
this.$container.classList.add('label-always-floats'); | ||
return; | ||
} | ||
this.$input.removeAttribute('placeholder'); | ||
this.$container.classList.remove('label-always-floats'); | ||
} | ||
/** | ||
@@ -104,0 +257,0 @@ * since classList.toggle with a second param is not supported in IE11 and below |
{ | ||
"name": "material-input", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "An easy drop-in material-input solution as a custom element", | ||
@@ -10,3 +10,3 @@ "main": "src/material-input.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "./node_modules/web-component-tester/bin/wct" | ||
}, | ||
@@ -34,5 +34,7 @@ "repository": { | ||
"gulp-babel": "^6.1.2", | ||
"gulp-sourcemaps": "^1.6.0" | ||
"gulp-sourcemaps": "^1.6.0", | ||
"include.js": "^0.1.2", | ||
"web-component-tester": "^4.3.5" | ||
}, | ||
"dependencies": {} | ||
} |
@@ -12,5 +12,5 @@ # Material-input | ||
Not released yet. Feel free to test it an contribute. | ||
```bash | ||
npm install --save material-input | ||
``` | ||
# npm install --save material-input | ||
``` | ||
@@ -26,4 +26,5 @@ You need the [webcomponents-lite polyfill](https://github.com/webcomponents/webcomponentsjs). | ||
## Usage | ||
Just drop the `<material-input>` element into you html and add your text. | ||
Just drop the `<material-input>` element into you html `<form>` element and you are ready to go. A visually hidden `<input>` field will be created which syncs its values with the `<material-input>` to allow for normal forms to pick up the value. | ||
```html | ||
@@ -47,8 +48,52 @@ <material-input name="username"></material-input> | ||
### Value | ||
Like a normal input field, you can set the value using the attribute `value` in html or via javascript by either setting the attribute or by directly setting the `value` property. | ||
```html | ||
<!-- html --> | ||
<material-input name="animal" value="cat"></material-input> | ||
``` | ||
```javascript | ||
// javascript | ||
document.querySelector('material-input.animal').value = 'cat'; | ||
document.querySelector('material-input.animal').setAttribute('value','cat'); | ||
``` | ||
### Validation | ||
Validation works just like with any default `<input>` element. Add a `required` or set the `type` to `email` and you will get the browsers validation notifications. Additionally the `material-input` will have a `valid` or `invalid` style. | ||
Additionally it is possible to explicitly set a field to be `invalid` by using the default `setCustomValidity` method on the `material-input`. You can read more about the [setCustomValidity feature on MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation#Controlling_the_text_of_constraints_violation). | ||
```javascript | ||
document.querySelector('material-input.customValidatedItem').setCustomValidity('This is not valid.'); | ||
``` | ||
#### Autovalidate | ||
When adding the `autovalidate` attribute to the `material-input`, the field will be validated on every `keydown` event. However, this means a field with no validation rules will always be valid and receive the `valid` immediately. | ||
## Custom styling | ||
Until browser support mixins, there is only basic styling. You can change the size of the text of all one one `material-input` which will increase its general size. | ||
Apart from basic styling on the `material-input` like `margins` or the `font-size` you can use the following `css properties` to for custom styling. | ||
Additionally you can set the font color for `material-input` which will change the color of the line below and label when active. | ||
### Limitation | ||
As of now only Chrome supports the `:host` element properly, even with the default polyfill. If you want to change the margin on the `material-input` you will need to wrap it in another element. | ||
```css | ||
/* select your specific input or all */ | ||
material-input.some-class{ | ||
/* the text color of the input */ | ||
--material-input-text-color: black; | ||
/* the text color of the placeholder or the floating label on an empty field */ | ||
--material-input-placeholder-color: grey; | ||
/* the color of the border and label when the field is focused */ | ||
--material-input-highlight-color: indigo; | ||
/* the color of the border and label when the field is in an invalid state */ | ||
--material-input-invalid-color: red; | ||
/* the color of the border and label when the field is in a valid state */ | ||
--material-input-valid-color: green; | ||
/* the default color of the bottom border */ | ||
--material-input-border-color: orange; | ||
/* the height of the bottom border when the field is not focused */ | ||
--material-input-line-height: 1px; | ||
/* the height of the bottom border when the field is focused */ | ||
--material-input-highlight-line-height: 3px; | ||
} | ||
``` |
'use strict'; | ||
// define attributes that are not supposed to be transferred | ||
// const attributesExceptions = [ | ||
// 'name', | ||
// 'label', | ||
// 'tabindex', | ||
// 'placeholder', | ||
// 'autofocus', | ||
// 'autocomplete', | ||
// 'autovalidate' | ||
// ]; | ||
class MaterialInput extends HTMLElement { | ||
@@ -10,8 +19,23 @@ | ||
createdCallback() { | ||
var value = ''; | ||
// set value of material-input | ||
Object.defineProperty( this, "value", { | ||
configurable: true, | ||
enumerable: true, | ||
get: function(){ | ||
return value; | ||
}, | ||
set: function(newValue){ | ||
value = !newValue ? '' : newValue; | ||
this._value(value); | ||
} | ||
}); | ||
this.createShadowRoot().innerHTML = ` | ||
<style> | ||
:host{ | ||
color: rgb(54,79,199); | ||
display: block; | ||
position: relative; | ||
background: transparent; | ||
margin: .5em 0; | ||
} | ||
@@ -22,3 +46,2 @@ .material-input__container{ | ||
position: relative; | ||
margin: .5em 0; | ||
} | ||
@@ -30,3 +53,3 @@ .material-input__input{ | ||
font-size: 1em; | ||
color: black; | ||
color: var(--material-input-text-color, black); | ||
padding: 1em 1em 1em 10px; | ||
@@ -36,11 +59,17 @@ display: block; | ||
border: none; | ||
border-bottom: .1rem solid rgb(206,212,218); | ||
border-bottom: var(--material-input-line-height, 1px) solid var(--material-input-border-color, rgb(206,212,218)); | ||
box-shadow: none; | ||
} | ||
.material-input__container.invalid .material-input__input{ | ||
border-bottom-color: var(--material-input-invalid-color, rgb(224,49,49)); | ||
} | ||
.material-input__container.valid .material-input__input{ | ||
border-bottom-color: var(--material-input-valid-color, rgb(47,158,68)); | ||
} | ||
.material-input__input:focus{ | ||
outline: none; | ||
color: black; | ||
} | ||
/* placeholder and placeholder fade on focus */ | ||
.material-input__input::-webkit-input-placeholder { | ||
color: rgb(134,142,150); | ||
color: var(--material-input-placeholder-color, rgb(134,142,150)); | ||
opacity: 1; | ||
@@ -53,3 +82,3 @@ } | ||
.material-input__input::-moz-placeholder { | ||
color: rgb(134,142,150); | ||
color: var(--material-input-placeholder-color, rgb(134,142,150)); | ||
opacity: 1; | ||
@@ -62,3 +91,3 @@ } | ||
.material-input__input:-ms-input-placeholder { | ||
color: rgb(134,142,150); | ||
color: var(--material-input-placeholder-color, rgb(134,142,150)); | ||
opacity: 1; | ||
@@ -80,4 +109,8 @@ } | ||
} | ||
.material-input__container.no-animation .material-input__label, | ||
.material-input__container.label-always-floats .material-input__label{ | ||
transition: 0s ease all; | ||
} | ||
.material-input__container.is-empty .material-input__input[placeholder] ~ .material-input__label{ | ||
color: black; | ||
color: var(--material-input-text-color, black); | ||
} | ||
@@ -93,4 +126,19 @@ /* active state */ | ||
.material-input__container.is-empty .material-input__input[placeholder]:focus ~ .material-input__label{ | ||
color: inherit; | ||
color: var(--material-input-highlight-color, rgb(54,79,199)); | ||
} | ||
/* errror state */ | ||
.material-input__container.invalid.label-always-floats .material-input__label, | ||
.material-input__container.invalid .material-input__input:focus ~ .material-input__label, | ||
.material-input__container.is-empty.invalid .material-input__input[placeholder]:focus ~ .material-input__label, | ||
.material-input__container.is-empty.invalid .material-input__input[placeholder] ~ .material-input__label{ | ||
color: var(--material-input-invalid-color, rgb(224,49,49)); | ||
} | ||
/* valid state */ | ||
.material-input__container.valid.label-always-floats .material-input__label, | ||
.material-input__container.valid .material-input__input:focus ~ .material-input__label, | ||
.material-input__container.is-empty.valid .material-input__input[placeholder]:focus ~ .material-input__label, | ||
.material-input__container.is-empty.valid .material-input__input[placeholder] ~ .material-input__label{ | ||
color: var(--material-input-valid-color, rgb(47,158,68)); | ||
} | ||
/* bar */ | ||
.material-input__bar{ | ||
@@ -103,9 +151,17 @@ position:relative; | ||
content:''; | ||
height:2px; | ||
height: var(--material-input-highlight-line-height, 2px); | ||
width:0; | ||
bottom:0; | ||
position:absolute; | ||
background: currentColor; | ||
background: var(--material-input-highlight-color, rgb(54,79,199)); | ||
transition:0.2s ease all; | ||
} | ||
.material-input__container.invalid .material-input__bar::before, | ||
.material-input__container.invalid .material-input__bar::after{ | ||
background: var(--material-input-invalid-color, rgb(224,49,49)); | ||
} | ||
.material-input__container.valid .material-input__bar::before, | ||
.material-input__container.valid .material-input__bar::after{ | ||
background: var(--material-input-valid-color, rgb(47,158,68)); | ||
} | ||
.material-input__bar::before { | ||
@@ -121,3 +177,3 @@ left:50%; | ||
</style> | ||
<div class="material-input__container"> | ||
<div class="material-input__container no-animation${this.value == '' ? ' is-empty' : ''}"> | ||
<input class="material-input__input" tabindex="-1" /> | ||
@@ -129,13 +185,20 @@ <label class="material-input__label"></label> | ||
`; | ||
// initialize attributes | ||
this.attributesExceptions = [ | ||
'name', | ||
'label', | ||
'tabindex', | ||
'placeholder', | ||
'autofocus', | ||
'autocomplete', | ||
'autovalidate' | ||
]; | ||
// set tab index to make element focussable | ||
this.setAttribute('tabindex',0); | ||
this.type = this.getAttribute('type') || 'text'; | ||
this.value = this.getAttribute('value') || null; | ||
this.name = this.getAttribute('name') || 'material-input'; | ||
this.label = this.getAttribute('label') || null; | ||
this.placeholder = this.getAttribute('placeholder') || null; | ||
this.invalid = this.hasAttribute('invalid') || false; | ||
this.valid = this.hasAttribute('valid') || false; | ||
// add input field for form submisson | ||
this.insertAdjacentHTML('afterend','<input tabindex="-1" type="hidden" name="'+this.name+'"/>'); | ||
// shim shadowDOM styling | ||
if(WebComponents !== undefined && WebComponents.flags.shadow === true){ | ||
WebComponents.ShadowCSS.shimStyling( this.shadowRoot, 'material-input' ) | ||
} | ||
// add hidden input | ||
this.insertAdjacentHTML('afterend','<input tabindex="-1" style="pointer-events: none; margin:0; border: 0; height: 0; opacity: 0; position: absolute; top: '+(this.offsetTop + this.offsetHeight)+'px; left: '+this.offsetLeft+'px;" name="'+this.getAttribute('name')+'"/>'); | ||
this.$hiddenInput = document.querySelector('input[name='+this.getAttribute('name')+']'); | ||
// elements | ||
@@ -146,38 +209,134 @@ this.$container = this.shadowRoot.querySelector('.material-input__container'); | ||
this.$message = this.$container.querySelector('.material-input__message'); | ||
this.$hiddenInput = document.querySelector('input[name='+this.name+']'); | ||
this.$input.setAttribute('type', this.type); | ||
// add events | ||
this._addEvents(); | ||
// transfer attribtues to input & hiddenInput | ||
this._transferAttributes(); | ||
// set value, label, etc. | ||
this._setValue(this.getAttribute('value')); | ||
this._setLabel(this.getAttribute('label')); | ||
this._setPlaceholder(this.getAttribute('placeholder')); | ||
this._setValid(); | ||
// remove no-animation loading class | ||
setTimeout(function(){ | ||
this.$container.classList.remove('no-animation'); | ||
}.bind(this),100); | ||
} | ||
/** | ||
* when an attribute is changed | ||
*/ | ||
attributeChangedCallback(attrName, oldVal, newVal) { | ||
// define callbacks | ||
var callbacks = { | ||
'valid': this._setValid, | ||
'value': this._setValue, | ||
'label': this._setLabel, | ||
'placeholder': this._setPlaceholder | ||
}; | ||
// call callback if it exists | ||
if(callbacks.hasOwnProperty(attrName)) { | ||
callbacks[attrName].call(this, newVal, oldVal); | ||
} | ||
else{ | ||
// if other attributes are updated, transfer updates to hidden input field | ||
this._transferAttribute(attrName, newVal, this.attributesExceptions); | ||
} | ||
} | ||
/** | ||
* set the custom validity of the input | ||
*/ | ||
setCustomValidity(msg){ | ||
this.$input.setCustomValidity(msg); | ||
this.$hiddenInput.setCustomValidity(msg) | ||
} | ||
/** | ||
* add events for all items | ||
*/ | ||
_addEvents(){ | ||
// on focuse pass to input | ||
this.addEventListener('focus', function(){ | ||
this.$input.focus(); | ||
}); | ||
// set validation status when hiddenInput is invalid | ||
this.$hiddenInput.addEventListener('invalid', function(){ | ||
this._setValid(false); | ||
}.bind(this)); | ||
// pass on value when user enters content | ||
this.$input.addEventListener('keydown', function(){ | ||
this._value(this.$input.value); | ||
}.bind(this)); | ||
// pass in value and validate when user exits input field | ||
this.$input.addEventListener('blur', function(){ | ||
this._value(this.$input.value); | ||
// check if is valid | ||
if(this.$input.value !== '' && (this.$input.validity.valid === true || this.$input.validity.valid === false)){ | ||
this._setValid(this.$input.validity.valid); | ||
} | ||
}.bind(this)); | ||
this.addEventListener('focus', function(){ | ||
this.$input.focus(); | ||
}); | ||
if(this.value){ | ||
this.$input.value = this.value; | ||
// if autovalidate is set to true, validate on key event | ||
if(this.hasAttribute('autovalidate') && String(this.getAttribute('autovalidate')) !== 'false'){ | ||
this.$input.addEventListener('keydown', function(){ | ||
// check if is valid | ||
if(this.$input.value !== '' && (this.$input.validity.valid === true)){ | ||
this._setValid(true); | ||
} | ||
}.bind(this)); | ||
} | ||
this._value(this.value); | ||
this._label(this.label); | ||
this._placeholder(this.placeholder); | ||
if(this.required){ | ||
this.$hiddenInput.setAttribute('required',''); | ||
} | ||
/** | ||
* set value | ||
*/ | ||
_setValue(newValue){ | ||
this.value = newValue; | ||
} | ||
/** | ||
* set field to valid or invalid | ||
*/ | ||
_setValid(validity = undefined){ | ||
// valid is not set | ||
if(!this.hasAttribute('valid') ){ | ||
this.valid = undefined; | ||
this.$container.classList.remove('valid'); | ||
this.$container.classList.remove('invalid'); | ||
} | ||
// valid is true | ||
if(this.getAttribute('valid') === 'true' || this.getAttribute('valid') === true || validity === true || validity === 'true'){ | ||
this.valid = true; | ||
this.$container.classList.add('valid'); | ||
this.$container.classList.remove('invalid'); | ||
} | ||
// valid is false | ||
if(this.getAttribute('valid') === 'false' || this.getAttribute('valid') === false || validity === false || validity === 'false') { | ||
this.valid = false; | ||
this.$container.classList.add('invalid'); | ||
this.$container.classList.remove('valid'); | ||
} | ||
} | ||
_value(value){ | ||
// set value of material-input | ||
this.value = !value ? '' : value; | ||
/** | ||
* transfer attributes to input | ||
*/ | ||
_transferAttributes(){ | ||
for(var key of Object.keys(this.attributes)){ | ||
if (this.attributes.hasOwnProperty(key)) { | ||
this._transferAttribute(this.attributes[key].name, this.attributes[key].value, this.attributesExceptions); | ||
} | ||
} | ||
} | ||
/** | ||
* transfer attribute to input | ||
*/ | ||
_transferAttribute(attrName, val, attributesExceptions){ | ||
if(attributesExceptions.indexOf(attrName) === -1){ | ||
this.$hiddenInput.setAttribute(attrName,val); | ||
this.$input.setAttribute(attrName,val); | ||
} | ||
} | ||
/** | ||
* update value and toggle is-empty class | ||
*/ | ||
_value(val){ | ||
// set value of hidden input for form submission | ||
this.$hiddenInput.value = this.value; | ||
this.$hiddenInput.value = val; | ||
this.$input.value = val; | ||
// set state depending on value | ||
this._toggle(this.$container, 'is-empty', this.value === ''); | ||
this._toggle(this.$container, 'is-empty', val === ''); | ||
} | ||
@@ -187,16 +346,20 @@ /** | ||
*/ | ||
_label(label){ | ||
this.$label.innerHTML = label; | ||
this._toggle(this.$container, 'label-always-floats',this.placeholder !== null); | ||
_setLabel(label){ | ||
if(label !== undefined && label !== null){ | ||
return this.$label.innerHTML = label; | ||
} | ||
this.$label.innerHTML = ''; | ||
} | ||
_placeholder(placeholder){ | ||
if(placeholder !== null){ | ||
this.$input.placeholder = placeholder; | ||
/** | ||
* set placeholder and add label-always-floats class | ||
*/ | ||
_setPlaceholder(placeholder){ | ||
if(placeholder !== null && placeholder !== undefined){ | ||
this.$input.setAttribute('placeholder', placeholder); | ||
this.$container.classList.add('label-always-floats'); | ||
return; | ||
} | ||
else{ | ||
this.$input.removeAttribute('placeholder'); | ||
} | ||
this.$input.removeAttribute('placeholder'); | ||
this.$container.classList.remove('label-always-floats'); | ||
} | ||
/** | ||
@@ -203,0 +366,0 @@ * since classList.toggle with a second param is not supported in IE11 and below |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
1031695
44
18456
1
97
0
6