blob: 459f0b8f28e6f332d348fc4c3326fc1d2c067ba6 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001/**
2 * @license
3 * Copyright 2016 Leif Olsen. 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 * This code is built with Google Material Design Lite,
18 * which is Licensed under the Apache License, Version 2.0
19 */
20
21/*
22 * Copied/Modified from https://github.com/google/material-design-lite/tree/master/src/textfield
23 */
24
25import { randomString } from '../utils/string-utils';
26import {
27 IS_DIRTY,
28 IS_FOCUSED,
29 IS_DISABLED,
30 IS_INVALID,
31 IS_UPGRADED
32} from '../utils/constants';
33
34(function() {
35 'use strict';
36 const LABEL = 'mdlext-selectfield__label';
37 const INPUT = 'mdlext-selectfield__select';
38
39 /**
40 * Class constructor for Selectfield MDLEXT component.
41 * Implements MDL component design pattern defined at:
42 * https://github.com/jasonmayes/mdl-component-design-pattern
43 *
44 * @constructor
45 * @param {HTMLElement} element The element that will be upgraded.
46 */
47 const MaterialExtSelectfield = function MaterialExtSelectfield(element) {
48 this.element_ = element;
49 this.init(); // Initialize instance.
50 };
51
52 window['MaterialExtSelectfield'] = MaterialExtSelectfield;
53
54 /**
55 * Handle focus.
56 *
57 * @param {Event} event The event that fired.
58 * @private
59 */
60 /*eslint no-unused-vars: 0*/
61 MaterialExtSelectfield.prototype.onFocus_ = function( /*event*/ ) {
62 this.element_.classList.add(IS_FOCUSED);
63 };
64
65 /**
66 * Handle lost focus.
67 *
68 * @param {Event} event The event that fired.
69 * @private
70 */
71 /*eslint no-unused-vars: 0*/
72 MaterialExtSelectfield.prototype.onBlur_ = function( /*event*/ ) {
73 this.element_.classList.remove(IS_FOCUSED);
74 };
75
76 /**
77 * Handle reset event from out side.
78 *
79 * @param {Event} event The event that fired.
80 * @private
81 */
82 MaterialExtSelectfield.prototype.onReset_ = function( /*event*/ ) {
83 this.updateClasses_();
84 };
85
86 /**
87 * Handle class updates.
88 *
89 * @private
90 */
91 MaterialExtSelectfield.prototype.updateClasses_ = function() {
92 this.checkDisabled();
93 this.checkValidity();
94 this.checkDirty();
95 this.checkFocus();
96 };
97
98 // Public methods.
99
100 /**
101 * Check the disabled state and update field accordingly.
102 *
103 * @public
104 */
105 MaterialExtSelectfield.prototype.checkDisabled = function() {
106 if (this.select_.disabled) {
107 this.element_.classList.add(IS_DISABLED);
108 } else {
109 this.element_.classList.remove(IS_DISABLED);
110 }
111 };
112 MaterialExtSelectfield.prototype['checkDisabled'] = MaterialExtSelectfield.prototype.checkDisabled;
113
114
115 /**
116 * Check the focus state and update field accordingly.
117 *
118 * @public
119 */
120 MaterialExtSelectfield.prototype.checkFocus = function() {
121 // Note: element.querySelector(':focus') always return null in JsDom, even if select element has focus
122 /*eslint no-extra-boolean-cast: 0*/
123 if (Boolean(this.element_.querySelector(':focus'))) {
124 this.element_.classList.add(IS_FOCUSED);
125 } else {
126 this.element_.classList.remove(IS_FOCUSED);
127 }
128 };
129
130 MaterialExtSelectfield.prototype['checkFocus'] = MaterialExtSelectfield.prototype.checkFocus;
131
132
133 /**
134 * Check the validity state and update field accordingly.
135 *
136 * @public
137 */
138 MaterialExtSelectfield.prototype.checkValidity = function() {
139
140 /* Don't think it makes any sense to check validity.
141 Tests I've done, so far, indicates that setting an illegal value via JS returns selectedIndex=0
142
143 if (this.select_.validity) {
144 if (this.select_.validity.valid) {
145 this.element_.classList.remove(this.CssClasses_.IS_INVALID);
146 } else {
147 this.element_.classList.add(this.CssClasses_.IS_INVALID);
148 }
149 }
150 */
151 };
152
153 MaterialExtSelectfield.prototype['checkValidity'] = MaterialExtSelectfield.prototype.checkValidity;
154
155 /**
156 * Check the dirty state and update field accordingly.
157 *
158 * @public
159 */
160 MaterialExtSelectfield.prototype.checkDirty = function() {
161 if (this.select_.value && this.select_.value.length > 0) {
162 this.element_.classList.add(IS_DIRTY);
163 } else {
164 this.element_.classList.remove(IS_DIRTY);
165 }
166 };
167
168 MaterialExtSelectfield.prototype['checkDirty'] = MaterialExtSelectfield.prototype.checkDirty;
169
170 /**
171 * Disable select field.
172 *
173 * @public
174 */
175 MaterialExtSelectfield.prototype.disable = function() {
176 this.select_.disabled = true;
177 this.updateClasses_();
178 };
179
180 MaterialExtSelectfield.prototype['disable'] = MaterialExtSelectfield.prototype.disable;
181
182 /**
183 * Enable select field.
184 *
185 * @public
186 */
187 MaterialExtSelectfield.prototype.enable = function() {
188 this.select_.disabled = false;
189 this.updateClasses_();
190 };
191
192 MaterialExtSelectfield.prototype['enable'] = MaterialExtSelectfield.prototype.enable;
193
194 /**
195 * Update select field value.
196 *
197 * @param {string} value The value to which to set the control (optional).
198 * @public
199 */
200 MaterialExtSelectfield.prototype.change = function(value) {
201 this.select_.value = value || '';
202 this.updateClasses_();
203 };
204 MaterialExtSelectfield.prototype['change'] = MaterialExtSelectfield.prototype.change;
205
206 /**
207 * Initialize element.
208 */
209 MaterialExtSelectfield.prototype.init = function() {
210 if (this.element_) {
211 this.label_ = this.element_.querySelector(`.${LABEL}`);
212 this.select_ = this.element_.querySelector(`.${INPUT}`);
213
214 if (this.select_) {
215 // Remove listeners, just in case ...
216 this.select_.removeEventListener('change', this.updateClasses_);
217 this.select_.removeEventListener('focus', this.onFocus_);
218 this.select_.removeEventListener('blur', this.onBlur_);
219 this.select_.removeEventListener('reset', this.onReset_);
220
221 this.select_.addEventListener('change', this.updateClasses_.bind(this));
222 this.select_.addEventListener('focus', this.onFocus_.bind(this));
223 this.select_.addEventListener('blur', this.onBlur_.bind(this));
224 this.select_.addEventListener('reset', this.onReset_.bind(this));
225
226 if(this.label_) {
227 let id;
228 if(!this.select_.hasAttribute('id')) {
229 id = `select-${randomString()}`;
230 this.select_.id = id;
231 }
232 else {
233 id = this.select_.id;
234 }
235
236 if(!this.label_.hasAttribute('for')) {
237 this.label_.setAttribute('for', id);
238 }
239 }
240
241 const invalid = this.element_.classList.contains(IS_INVALID);
242 this.updateClasses_();
243 this.element_.classList.add(IS_UPGRADED);
244
245 if (invalid) {
246 this.element_.classList.add(IS_INVALID);
247 }
248 if (this.select_.hasAttribute('autofocus')) {
249 this.element_.focus();
250 this.checkFocus();
251 }
252 }
253 }
254 };
255
256 /**
257 * Downgrade component
258 * E.g remove listeners and clean up resources
259 *
260 * Nothing to downgrade
261 *
262 MaterialExtSelectfield.prototype.mdlDowngrade_ = function() {
263 'use strict';
264 console.log('***** MaterialExtSelectfield.mdlDowngrade ');
265 };
266 */
267
268 // The component registers itself. It can assume componentHandler is available
269 // in the global scope.
270 /*eslint no-undef: 0*/
271 componentHandler.register({
272 constructor: MaterialExtSelectfield,
273 classAsString: 'MaterialExtSelectfield',
274 cssClass: 'mdlext-js-selectfield',
275 widget: true
276 });
277})();