UNPKG

5.88 kBJavaScriptView Raw
1/*
2Copyright 2013-2015 ASIAL CORPORATION
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6 http://www.apache.org/licenses/LICENSE-2.0
7Unless required by applicable law or agreed to in writing, software
8distributed under the License is distributed on an "AS IS" BASIS,
9WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10See the License for the specific language governing permissions and
11limitations under the License.
12*/
13
14import onsElements from '../ons/elements.js';
15import util from '../ons/util.js';
16import BaseInputElement from './base/base-input.js';
17
18const scheme = {
19 '': 'range--*',
20 '.range__input': 'range--*__input',
21 '.range__focus-ring': 'range--*__focus-ring'
22};
23
24const activeClassToken = 'range__input--active';
25
26/**
27 * @element ons-range
28 * @category form
29 * @modifier material
30 * [en]Material Design slider[/en]
31 * [ja][/ja]
32 * @description
33 * [en]
34 * Range input component. Used to display a draggable slider.
35 *
36 * Works very similar to the `<input type="range">` element.
37 * [/en]
38 * [ja][/ja]
39 * @codepen xZQomM
40 * @tutorial vanilla/Reference/range
41 * @guide theming.html#modifiers [en]More details about the `modifier` attribute[/en][ja]modifier属性の使い方[/ja]
42 * @seealso ons-input
43 * [en]The `<ons-input>` component is used to display text inputs, radio buttons and checkboxes.[/en]
44 * [ja][/ja]
45 * @example
46 * <ons-range value="20"></ons-range>
47 * <ons-range modifier="material" value="10"></range>
48 */
49
50export default class RangeElement extends BaseInputElement {
51
52 constructor() {
53 super();
54
55 this._onMouseDown = this._onMouseDown.bind(this);
56 this._onMouseUp = this._onMouseUp.bind(this);
57 this._onTouchStart = this._onTouchStart.bind(this);
58 this._onTouchEnd = this._onTouchEnd.bind(this);
59 this._onInput = this._update.bind(this);
60 this._onDragstart = this._onDragstart.bind(this);
61 this._onDragend = this._onDragend.bind(this);
62 }
63
64 _compile() {
65 super._compile();
66 this._updateDisabled(this.hasAttribute('disabled'));
67 }
68
69 /* Inherited props */
70
71 _update() {
72 const input = this._input;
73 const focusRing = this._focusRing;
74
75 input.style.backgroundSize = `${100 * this._ratio}% 2px`;
76 focusRing.value = this.value;
77
78 // NOTE: "_zero" attribute is used for CSS styling.
79 if ((input.min === '' && input.value === '0') || input.min === input.value) {
80 input.setAttribute('_zero', '');
81 } else {
82 input.removeAttribute('_zero');
83 }
84
85 ['min', 'max'].forEach(attr => focusRing[attr] = input[attr]);
86 }
87
88 get _scheme() {
89 return scheme;
90 }
91
92 get _template() {
93 return `
94 <input type="${this.type}" class="${this._defaultClassName}__input">
95 <input type="range" class="range__focus-ring" tabIndex="-1">
96 `;
97 }
98
99 get _defaultClassName() {
100 return 'range';
101 }
102
103 get type() {
104 return 'range';
105 }
106
107 /* Own props */
108
109 _onMouseDown(e) {
110 this._input.classList.add(activeClassToken);
111 setImmediate(() => this._input.focus());
112 }
113
114 _onTouchStart(e) {
115 this._onMouseDown();
116 }
117
118 _onMouseUp(e) {
119 this._input.classList.remove(activeClassToken);
120 }
121
122 _onTouchEnd(e) {
123 this._onMouseUp(e);
124 }
125
126 _onDragstart(e) {
127 e.consumed = true;
128 e.gesture.stopPropagation();
129 this._input.classList.add(activeClassToken);
130 this.addEventListener('drag', this._onDrag);
131 }
132
133 _onDrag(e) {
134 e.stopPropagation();
135 }
136
137 _onDragend(e) {
138 this._input.classList.remove(activeClassToken);
139 this.removeEventListener('drag', this._onDrag);
140 }
141
142 get _focusRing() {
143 return this.children[1];
144 }
145
146 get _ratio() {
147 // Returns the current ratio.
148 const min = this._input.min === '' ? 0 : parseInt(this._input.min);
149 const max = this._input.max === '' ? 100 : parseInt(this._input.max);
150
151 return (this.value - min) / (max - min);
152 }
153
154 static get observedAttributes() {
155 return ['disabled', ...BaseInputElement.observedAttributes];
156 }
157
158 attributeChangedCallback(name, last, current) {
159 if (name === 'disabled') {
160 this._updateDisabled(current);
161 }
162 super.attributeChangedCallback(name, last, current);
163 }
164
165 /**
166 * @param {boolean} disabled
167 */
168 _updateDisabled(disabled) {
169 if (disabled) {
170 this.classList.add('range--disabled');
171 } else {
172 this.classList.remove('range--disabled');
173 }
174 }
175
176 connectedCallback() {
177 this._setupListeners(true);
178 }
179
180 disconnectedCallback() {
181 this._setupListeners(false);
182 }
183
184 _setupListeners(add) {
185 const action = (add ? 'add' : 'remove') + 'EventListener';
186 util[action](this, 'touchstart', this._onTouchStart, { passive: true });
187 this[action]('mousedown', this._onMouseDown);
188 this[action]('mouseup', this._onMouseUp);
189 this[action]('touchend', this._onTouchEnd);
190 this[action]('dragstart', this._onDragstart);
191 this[action]('dragend', this._onDragend);
192 this[action]('input', this._onInput);
193 }
194
195 /**
196 * @attribute disabled
197 * @description
198 * [en]Whether the element is disabled or not.[/en]
199 * [ja]無効化されている場合に`true`。[/ja]
200 */
201
202 /**
203 * @property disabled
204 * @type {Boolean}
205 * @description
206 * [en]Whether the element is disabled or not.[/en]
207 * [ja]無効化されている場合に`true`。[/ja]
208 */
209
210 /**
211 * @property value
212 * @type {Number}
213 * @description
214 * [en]Current value.[/en]
215 * [ja][/ja]
216 */
217
218 /**
219 * @method focus
220 * @signature focus()
221 * @description
222 * [en]Focuses the range.[/en]
223 * [ja][/ja]
224 */
225
226 /**
227 * @method blur
228 * @signature blur()
229 * @description
230 * [en]Removes focus from the range.[/en]
231 * [ja][/ja]
232 */
233}
234
235onsElements.Range = RangeElement;
236customElements.define('ons-range', RangeElement);