-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add vaadin-number-field element (#110)
* Implement number-field * Include number-field demos in demos suite * Align the name of the file with number field demo with other demos * The reference to native input has changed. Fixed tests * move number field to src, and create its lumo file * Update screenshots * Validate against min, max. Synchronize input value * Disable buttons on readonly * Fix min/max limits when set to zero * Simply code by reducing conditional blocks * Disable buttons when limits have been reached * Align with master * Adding step feature and focus input on click. * Improve vaadin-number-field (#292) * Improve vaadin-number-field * Fix value control buttons focusing behavior for touch * Add browser native validation behaviour
- Loading branch information
1 parent
424d5be
commit 9bed48b
Showing
22 changed files
with
676 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
<dom-module id="number-field-demos"> | ||
<template> | ||
<style include="vaadin-component-demo-shared-styles"> | ||
:host { | ||
display: block; | ||
} | ||
</style> | ||
|
||
<p>The <code><vaadin-number-field></code> element has all the same features as the <code><vaadin-text-field></code> element plus number validation and the additional features listed on this page.</p> | ||
|
||
|
||
<h3>Basic number field</h3> | ||
<vaadin-demo-snippet id="number-field-demos-basic"> | ||
<template preserve-content> | ||
<vaadin-number-field label="Years of expertise"></vaadin-number-field> | ||
</template> | ||
</vaadin-demo-snippet> | ||
|
||
<h3>Validation</h3> | ||
<vaadin-demo-snippet id="number-field-demos-basic-valiation"> | ||
<template preserve-content> | ||
<vaadin-number-field label="Years of expertise" required error-message="Please enter your years of expertise"></vaadin-number-field> | ||
</template> | ||
</vaadin-demo-snippet> | ||
|
||
<h3>Number field with controls</h3> | ||
<vaadin-demo-snippet id="number-field-demos-with-controls"> | ||
<template preserve-content> | ||
<vaadin-number-field has-controls></vaadin-number-field> | ||
</template> | ||
</vaadin-demo-snippet> | ||
|
||
<h3>Number field with value limits</h3> | ||
<vaadin-demo-snippet id="number-field-demos-with-limits"> | ||
<template preserve-content> | ||
<vaadin-number-field value="1" min="1" max="10" has-controls></vaadin-number-field> | ||
</template> | ||
</vaadin-demo-snippet> | ||
|
||
<h3>Number field with step</h3> | ||
<vaadin-demo-snippet id="number-field-demos-with-step"> | ||
<template preserve-content> | ||
<vaadin-number-field step="0.2" min="0" max="10" has-controls></vaadin-number-field> | ||
</template> | ||
</vaadin-demo-snippet> | ||
|
||
</template> | ||
<script> | ||
class NumberFieldDemos extends DemoReadyEventEmitter(TextFieldDemo(Polymer.Element)) { | ||
static get is() { | ||
return 'number-field-demos'; | ||
} | ||
} | ||
customElements.define(NumberFieldDemos.is, NumberFieldDemos); | ||
</script> | ||
</dom-module> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,245 @@ | ||
<!-- | ||
@license | ||
Copyright (c) 2017 Vaadin Ltd. | ||
This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
--> | ||
|
||
<link rel="import" href="../../polymer/polymer-element.html"> | ||
<link rel="import" href="../../polymer/lib/elements/custom-style.html"> | ||
<link rel="import" href="vaadin-text-field.html"> | ||
|
||
<dom-module id="vaadin-number-field-template"> | ||
<template> | ||
<style> | ||
:host([readonly]) { | ||
pointer-events: none; | ||
} | ||
|
||
[part="decrease-button"]::before { | ||
content: "−"; | ||
} | ||
|
||
[part="increase-button"]::before { | ||
content: "+"; | ||
} | ||
|
||
[part="decrease-button"], | ||
[part="increase-button"] { | ||
-webkit-user-select: none; | ||
-moz-user-select: none; | ||
-ms-user-select: none; | ||
user-select: none; | ||
} | ||
|
||
/* Hide the native arrow icons */ | ||
[part="value"]::-webkit-outer-spin-button, | ||
[part="value"]::-webkit-inner-spin-button { | ||
-webkit-appearance: none; | ||
margin: 0; | ||
} | ||
|
||
[part="value"] { | ||
/* Older Firefox versions (v47.0) requires !important */ | ||
-moz-appearance: textfield !important; | ||
} | ||
</style> | ||
|
||
<div | ||
disabled$="[[!_allowed(-1, value, min, max)]]" | ||
part="decrease-button" | ||
on-click="_decreaseValue" | ||
on-touchend="_decreaseButtonTouchend" | ||
hidden$="[[!hasControls]]"> | ||
</div> | ||
|
||
<div | ||
disabled$="[[!_allowed(1, value, min, max)]]" | ||
part="increase-button" | ||
on-click="_increaseValue" | ||
on-touchend="_increaseButtonTouchend" | ||
hidden$="[[!hasControls]]"> | ||
</div> | ||
</template> | ||
|
||
<script> | ||
(function() { | ||
let memoizedTemplate; | ||
|
||
/** | ||
* `<vaadin-number-field>` is a Polymer 2 element for number field control in forms. | ||
* | ||
* ```html | ||
* <vaadin-number-field label="Number"> | ||
* </vaadin-number-field> | ||
* ``` | ||
* | ||
* @memberof Vaadin | ||
* @extends Vaadin.TextFieldElement | ||
* @demo demo/index.html | ||
*/ | ||
class NumberFieldElement extends Vaadin.TextFieldElement { | ||
static get is() { | ||
return 'vaadin-number-field'; | ||
} | ||
|
||
static get properties() { | ||
return { | ||
/** | ||
* Set to true to display value increase/decrease controls. | ||
*/ | ||
hasControls: { | ||
type: Boolean, | ||
value: false, | ||
reflectToAttribue: true | ||
}, | ||
|
||
/** | ||
* The minimum value of the field. | ||
*/ | ||
min: { | ||
type: Number, | ||
reflectToAttribue: true, | ||
observer: '_minChanged' | ||
}, | ||
|
||
/** | ||
* The maximum value of the field. | ||
*/ | ||
max: { | ||
type: Number, | ||
reflectToAttribue: true, | ||
observer: '_maxChanged' | ||
}, | ||
|
||
/** | ||
* Specifies the allowed number intervals of the field. | ||
*/ | ||
step: { | ||
reflectToAttribue: true, | ||
observer: '_stepChanged', | ||
value: 1 | ||
} | ||
|
||
}; | ||
} | ||
|
||
ready() { | ||
super.ready(); | ||
this.__previousValidInput = this.value || ''; | ||
this.focusElement.type = 'number'; | ||
this.focusElement.addEventListener('change', this.__onInputChange.bind(this)); | ||
} | ||
|
||
_decreaseButtonTouchend(e) { | ||
// Cancel the following click and focus events | ||
e.preventDefault(); | ||
this._decreaseValue(); | ||
} | ||
|
||
_increaseButtonTouchend(e) { | ||
// Cancel the following click and focus events | ||
e.preventDefault(); | ||
this._increaseValue(); | ||
} | ||
|
||
static get template() { | ||
if (!memoizedTemplate) { | ||
// Clone the superclass template | ||
memoizedTemplate = super.template.cloneNode(true); | ||
|
||
// Retrieve this element's dom-module template | ||
const thisTemplate = Polymer.DomModule.import(this.is + '-template', 'template'); | ||
const decreaseButton = thisTemplate.content.querySelector('[part="decrease-button"]'); | ||
const increaseButton = thisTemplate.content.querySelector('[part="increase-button"]'); | ||
const styles = thisTemplate.content.querySelector('style'); | ||
|
||
// Add the buttons and styles to the text-field template | ||
const inputField = memoizedTemplate.content.querySelector('[part="input-field"]'); | ||
const prefixSlot = memoizedTemplate.content.querySelector('[name="prefix"]'); | ||
inputField.insertBefore(decreaseButton, prefixSlot); | ||
inputField.appendChild(increaseButton); | ||
memoizedTemplate.content.appendChild(styles); | ||
|
||
return memoizedTemplate; | ||
} | ||
} | ||
|
||
_decreaseValue() { | ||
this._allowed(-1) && this.__add(-1); | ||
} | ||
|
||
_increaseValue() { | ||
this._allowed(1) && this.__add(1); | ||
} | ||
|
||
__add(sign) { | ||
const incr = sign * (this.step || 1); | ||
// Behave like native number input adjusting to the next exact multiple of step. | ||
this.value = this.focusElement.value = | ||
(incr + (incr * Math.floor((parseFloat(this.value || 0) / incr).toFixed(1)))).toFixed(this.__decimals); | ||
this.dispatchEvent(new CustomEvent('change', {bubbles: true})); | ||
} | ||
|
||
_allowed(sign, value, min, max) { | ||
const incr = sign * (this.step || 1); | ||
return !this.disabled && (incr < 0 | ||
? this.min == null || this.value > this.min | ||
: this.max == null || this.value < this.max); | ||
} | ||
|
||
_minChanged() { | ||
this.focusElement.min = this.min; | ||
} | ||
|
||
_maxChanged() { | ||
this.focusElement.max = this.max; | ||
} | ||
|
||
_valueChanged(newVal, oldVal) { | ||
// Validate value to be numeric | ||
if (newVal && isNaN(parseFloat(newVal).toFixed(this.__decimals))) { | ||
this.value = ''; | ||
} else if (!isNaN(parseFloat(this.value)) && | ||
parseFloat(this.value) !== parseFloat(parseFloat(this.value).toFixed(this.__decimals))) { | ||
// Validate correct decimals | ||
this.value = parseFloat(parseFloat(this.value).toFixed(this.__decimals)); | ||
} | ||
|
||
super._valueChanged(this.value, oldVal); | ||
} | ||
|
||
__onInputChange() { | ||
this.checkValidity() && this.__adjustDecimals(); | ||
} | ||
|
||
__adjustDecimals() { | ||
// when step is not an integer, adjust decimals. | ||
this.focusElement.value && (this.value = parseFloat(this.focusElement.value).toFixed(this.__decimals)); | ||
} | ||
|
||
_stepChanged(step) { | ||
this.focusElement.step = step; | ||
// Compute number of dedimals to display in input based on provided step | ||
this.__decimals = String(step).replace(/^\d*\.?(.*)?$/, '$1').length; | ||
this.__adjustDecimals(); | ||
} | ||
|
||
checkValidity() { | ||
// text-field mixin does not check against `min` and `max` | ||
if (this.min !== undefined || this.max !== undefined) { | ||
this.invalid = !this.focusElement.checkValidity(); | ||
} | ||
return super.checkValidity(); | ||
} | ||
} | ||
|
||
window.customElements.define(NumberFieldElement.is, NumberFieldElement); | ||
|
||
/** | ||
* @namespace Vaadin | ||
*/ | ||
window.Vaadin = window.Vaadin || {}; | ||
Vaadin.NumberFieldElement = NumberFieldElement; | ||
})(); | ||
</script> | ||
</dom-module> |
Oops, something went wrong.