npm-polymer-elements
Version:
Polymer Elements package for npm
246 lines (201 loc) • 7.03 kB
HTML
<!--
@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../iron-flex-layout/iron-flex-layout.html">
<link rel="import" href="../paper-input/paper-input-behavior.html">
<link rel="import" href="../paper-input/paper-input-container.html">
<link rel="import" href="../paper-input/paper-input-error.html">
<link rel="import" href="../iron-input/iron-input.html">
<link rel="import" href="../iron-form-element-behavior/iron-form-element-behavior.html">
<link rel="import" href="../iron-icon/iron-icon.html">
<script src="cc-validator.js"></script>
<!--
`gold-cc-input` is a single-line text field with Material Design styling
for entering a credit card number. As the user types, the number will be
formatted by adding a space every 4 digits.
<gold-cc-input></gold-cc-input>
It may include an optional label, which by default is "Card number".
<gold-cc-input label="CC"></gold-cc-input>
### Validation
The input can detect whether a credit card number is valid, and the type
of credit card it is, using the Luhn checksum. See `http://jquerycreditcardvalidator.com/`
for more information.
The input can be automatically validated as the user is typing by using
the `auto-validate` and `required` attributes. For manual validation, the
element also has a `validate()` method, which returns the validity of the
input as well sets any appropriate error messages and styles.
See `Polymer.PaperInputBehavior` for more API docs.
### Styling
See `Polymer.PaperInputContainer` for a list of custom properties used to
style this element.
@group Gold Elements
@hero hero.svg
@demo demo/index.html
@class gold-cc-input
-->
<dom-module id="gold-cc-input">
<style>
:host {
display: block;
}
/* Use a container so that when hiding the icon, the layout doesn't jump around. */
.icon-container {
margin-left: 10px;
height: 27px;
}
iron-icon {
--iron-icon-width: 40px;
--iron-icon-height: 24px;
}
.container {
@apply(--layout-horizontal);
}
</style>
<template>
<paper-input-container id="container"
disabled$="[[disabled]]"
no-label-float="[[noLabelFloat]]"
always-float-label="[[_computeAlwaysFloatLabel(alwaysFloatLabel,placeholder)]]"
invalid="[[invalid]]">
<label hidden$="[[!label]]">[[label]]</label>
<div class="container">
<input is="iron-input" id="input"
aria-labelledby$="[[_ariaLabelledBy]]"
aria-describedby$="[[_ariaDescribedBy]]"
bind-value="{{value}}"
type="tel"
maxlength="30"
required$="[[required]]"
allowed-pattern="[0-9 ]"
prevent-invalid-input
autocomplete="cc-number"
name$="[[name]]"
disabled$="[[disabled]]"
invalid="{{invalid}}"
autofocus$="[[autofocus]]"
inputmode$="[[inputmode]]"
placeholder$="[[placeholder]]"
readonly$="[[readonly]]"
size$="[[size]]">
<div class="icon-container"><iron-icon id="icon"></iron-icon></div>
</div>
<template is="dom-if" if="[[errorMessage]]">
<paper-input-error>[[errorMessage]]</paper-input-error>
</template>
</paper-input-container>
</template>
</dom-module>
<script>
(function() {
Polymer({
is: 'gold-cc-input',
behaviors: [
Polymer.PaperInputBehavior,
Polymer.IronValidatableBehavior,
Polymer.IronFormElementBehavior
],
properties: {
/**
* The label for this input.
*/
label: {
type: String,
value: "Card number"
},
/**
* The type of the credit card, if it is valid. Empty otherwise.
*/
cardType: {
type: String,
notify: true
},
value: {
observer: '_onValueChanged'
}
},
observers: [
'_onFocusedChanged(focused)'
],
ready: function() {
// If there's an initial input, validate it.
if (this.value)
this._handleAutoValidate();
},
/**
* A handler that is called on input
*/
_onValueChanged: function(value, oldValue) {
// The initial property assignment is handled by `ready`.
if (oldValue == undefined)
return;
var start = this.$.input.selectionStart;
var previousCharASpace = value ? this.value.charAt(start - 1) == ' ' : false;
var value = value.replace(/\s+/g, '');
var formattedValue = '';
for (var i = 0; i < value.length; i++) {
// Add a space after every 4 characters.
if ((i != 0) && (i % 4 == 0)) {
formattedValue += ' ';
}
formattedValue += value[i];
}
this.updateValueAndPreserveCaret(formattedValue.trim());
// If the character right before the selection is a newly inserted
// space, we need to advance the selection to maintain the caret position.
if (!previousCharASpace && this.value.charAt(start - 1) == ' ') {
this.$.input.selectionStart = start+1;
this.$.input.selectionEnd = start+1;
}
this._handleAutoValidate();
},
/**
* Returns true if the element has a valid value, and sets the visual
* error state.
*
* @return {boolean} Whether the input is currently valid or not.
*/
validate: function() {
// Empty, non-required input is valid.
if (!this.required && this.value == '') {
return true;
}
var result = CreditCardValidator.validate(this.value);
var valid = result.valid && result.length_valid;
this.cardType = valid ? result.card_type.name : '';
// Update the container and its addons (i.e. the custom error-message).
this.$.container.invalid = !valid;
this.$.container.updateAddons({
inputElement: this.$.input,
value: this.value,
invalid: !valid
});
// We don't have icons for all the card types.
if (valid && result.card_type.icon) {
this.$.icon.src = this.resolveUrl(result.card_type.icon);
this.$.icon.alt = this.cardType;
this.$.icon.hidden = false;
} else {
this.$.icon.src = null;
this.$.icon.alt = '';
this.$.icon.hidden = true;
}
return valid;
},
/**
* Overidden from Polymer.IronControlState.
*/
_onFocusedChanged: function(focused) {
if (!focused) {
this._handleAutoValidate();
}
}
})
})();
</script>