blob: 5d5a17c6d6df0dafb0aacf0190985d58be88b795 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001/**
2 * @license
3 * Copyright 2015 Google Inc. All Rights Reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18(function() {
19 'use strict';
20
21 /**
22 * Class constructor for icon toggle MDL component.
23 * Implements MDL component design pattern defined at:
24 * https://github.com/jasonmayes/mdl-component-design-pattern
25 *
26 * @constructor
27 * @param {HTMLElement} element The element that will be upgraded.
28 */
29 var MaterialIconToggle = function MaterialIconToggle(element) {
30 this.element_ = element;
31
32 // Initialize instance.
33 this.init();
34 };
35 window['MaterialIconToggle'] = MaterialIconToggle;
36
37 /**
38 * Store constants in one place so they can be updated easily.
39 *
40 * @enum {string | number}
41 * @private
42 */
43 MaterialIconToggle.prototype.Constant_ = {
44 TINY_TIMEOUT: 0.001
45 };
46
47 /**
48 * Store strings for class names defined by this component that are used in
49 * JavaScript. This allows us to simply change it in one place should we
50 * decide to modify at a later date.
51 *
52 * @enum {string}
53 * @private
54 */
55 MaterialIconToggle.prototype.CssClasses_ = {
56 INPUT: 'mdl-icon-toggle__input',
57 JS_RIPPLE_EFFECT: 'mdl-js-ripple-effect',
58 RIPPLE_IGNORE_EVENTS: 'mdl-js-ripple-effect--ignore-events',
59 RIPPLE_CONTAINER: 'mdl-icon-toggle__ripple-container',
60 RIPPLE_CENTER: 'mdl-ripple--center',
61 RIPPLE: 'mdl-ripple',
62 IS_FOCUSED: 'is-focused',
63 IS_DISABLED: 'is-disabled',
64 IS_CHECKED: 'is-checked'
65 };
66
67 /**
68 * Handle change of state.
69 *
70 * @param {Event} event The event that fired.
71 * @private
72 */
73 MaterialIconToggle.prototype.onChange_ = function(event) {
74 this.updateClasses_();
75 };
76
77 /**
78 * Handle focus of element.
79 *
80 * @param {Event} event The event that fired.
81 * @private
82 */
83 MaterialIconToggle.prototype.onFocus_ = function(event) {
84 this.element_.classList.add(this.CssClasses_.IS_FOCUSED);
85 };
86
87 /**
88 * Handle lost focus of element.
89 *
90 * @param {Event} event The event that fired.
91 * @private
92 */
93 MaterialIconToggle.prototype.onBlur_ = function(event) {
94 this.element_.classList.remove(this.CssClasses_.IS_FOCUSED);
95 };
96
97 /**
98 * Handle mouseup.
99 *
100 * @param {Event} event The event that fired.
101 * @private
102 */
103 MaterialIconToggle.prototype.onMouseUp_ = function(event) {
104 this.blur_();
105 };
106
107 /**
108 * Handle class updates.
109 *
110 * @private
111 */
112 MaterialIconToggle.prototype.updateClasses_ = function() {
113 this.checkDisabled();
114 this.checkToggleState();
115 };
116
117 /**
118 * Add blur.
119 *
120 * @private
121 */
122 MaterialIconToggle.prototype.blur_ = function() {
123 // TODO: figure out why there's a focus event being fired after our blur,
124 // so that we can avoid this hack.
125 window.setTimeout(function() {
126 this.inputElement_.blur();
127 }.bind(this), /** @type {number} */ (this.Constant_.TINY_TIMEOUT));
128 };
129
130 // Public methods.
131
132 /**
133 * Check the inputs toggle state and update display.
134 *
135 * @public
136 */
137 MaterialIconToggle.prototype.checkToggleState = function() {
138 if (this.inputElement_.checked) {
139 this.element_.classList.add(this.CssClasses_.IS_CHECKED);
140 } else {
141 this.element_.classList.remove(this.CssClasses_.IS_CHECKED);
142 }
143 };
144 MaterialIconToggle.prototype['checkToggleState'] =
145 MaterialIconToggle.prototype.checkToggleState;
146
147 /**
148 * Check the inputs disabled state and update display.
149 *
150 * @public
151 */
152 MaterialIconToggle.prototype.checkDisabled = function() {
153 if (this.inputElement_.disabled) {
154 this.element_.classList.add(this.CssClasses_.IS_DISABLED);
155 } else {
156 this.element_.classList.remove(this.CssClasses_.IS_DISABLED);
157 }
158 };
159 MaterialIconToggle.prototype['checkDisabled'] =
160 MaterialIconToggle.prototype.checkDisabled;
161
162 /**
163 * Disable icon toggle.
164 *
165 * @public
166 */
167 MaterialIconToggle.prototype.disable = function() {
168 this.inputElement_.disabled = true;
169 this.updateClasses_();
170 };
171 MaterialIconToggle.prototype['disable'] =
172 MaterialIconToggle.prototype.disable;
173
174 /**
175 * Enable icon toggle.
176 *
177 * @public
178 */
179 MaterialIconToggle.prototype.enable = function() {
180 this.inputElement_.disabled = false;
181 this.updateClasses_();
182 };
183 MaterialIconToggle.prototype['enable'] = MaterialIconToggle.prototype.enable;
184
185 /**
186 * Check icon toggle.
187 *
188 * @public
189 */
190 MaterialIconToggle.prototype.check = function() {
191 this.inputElement_.checked = true;
192 this.updateClasses_();
193 };
194 MaterialIconToggle.prototype['check'] = MaterialIconToggle.prototype.check;
195
196 /**
197 * Uncheck icon toggle.
198 *
199 * @public
200 */
201 MaterialIconToggle.prototype.uncheck = function() {
202 this.inputElement_.checked = false;
203 this.updateClasses_();
204 };
205 MaterialIconToggle.prototype['uncheck'] =
206 MaterialIconToggle.prototype.uncheck;
207
208 /**
209 * Initialize element.
210 */
211 MaterialIconToggle.prototype.init = function() {
212
213 if (this.element_) {
214 this.inputElement_ =
215 this.element_.querySelector('.' + this.CssClasses_.INPUT);
216
217 if (this.element_.classList.contains(this.CssClasses_.JS_RIPPLE_EFFECT)) {
218 this.element_.classList.add(this.CssClasses_.RIPPLE_IGNORE_EVENTS);
219 this.rippleContainerElement_ = document.createElement('span');
220 this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CONTAINER);
221 this.rippleContainerElement_.classList.add(this.CssClasses_.JS_RIPPLE_EFFECT);
222 this.rippleContainerElement_.classList.add(this.CssClasses_.RIPPLE_CENTER);
223 this.boundRippleMouseUp = this.onMouseUp_.bind(this);
224 this.rippleContainerElement_.addEventListener('mouseup', this.boundRippleMouseUp);
225
226 var ripple = document.createElement('span');
227 ripple.classList.add(this.CssClasses_.RIPPLE);
228
229 this.rippleContainerElement_.appendChild(ripple);
230 this.element_.appendChild(this.rippleContainerElement_);
231 }
232
233 this.boundInputOnChange = this.onChange_.bind(this);
234 this.boundInputOnFocus = this.onFocus_.bind(this);
235 this.boundInputOnBlur = this.onBlur_.bind(this);
236 this.boundElementOnMouseUp = this.onMouseUp_.bind(this);
237 this.inputElement_.addEventListener('change', this.boundInputOnChange);
238 this.inputElement_.addEventListener('focus', this.boundInputOnFocus);
239 this.inputElement_.addEventListener('blur', this.boundInputOnBlur);
240 this.element_.addEventListener('mouseup', this.boundElementOnMouseUp);
241
242 this.updateClasses_();
243 this.element_.classList.add('is-upgraded');
244 }
245 };
246
247 // The component registers itself. It can assume componentHandler is available
248 // in the global scope.
249 componentHandler.register({
250 constructor: MaterialIconToggle,
251 classAsString: 'MaterialIconToggle',
252 cssClass: 'mdl-js-icon-toggle',
253 widget: true
254 });
255})();