@materializecss/materialize 1.1.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/Gruntfile.js +38 -24
  2. package/LICENSE +21 -21
  3. package/README.md +91 -97
  4. package/dist/css/materialize.css +8608 -8631
  5. package/dist/css/materialize.min.css +12 -12
  6. package/dist/js/materialize.js +12816 -12669
  7. package/dist/js/materialize.min.js +6 -6
  8. package/extras/noUiSlider/nouislider.css +404 -406
  9. package/extras/noUiSlider/nouislider.js +2147 -2147
  10. package/extras/noUiSlider/nouislider.min.js +0 -0
  11. package/js/anime.min.js +34 -34
  12. package/js/autocomplete.js +479 -479
  13. package/js/buttons.js +354 -354
  14. package/js/cards.js +40 -40
  15. package/js/carousel.js +732 -732
  16. package/js/cash.js +960 -960
  17. package/js/characterCounter.js +136 -136
  18. package/js/chips.js +486 -486
  19. package/js/collapsible.js +275 -275
  20. package/js/component.js +44 -44
  21. package/js/datepicker.js +983 -983
  22. package/js/dropdown.js +669 -669
  23. package/js/forms.js +285 -275
  24. package/js/global.js +428 -424
  25. package/js/materialbox.js +453 -453
  26. package/js/modal.js +382 -382
  27. package/js/parallax.js +138 -138
  28. package/js/pushpin.js +148 -148
  29. package/js/range.js +263 -263
  30. package/js/scrollspy.js +295 -295
  31. package/js/select.js +391 -310
  32. package/js/sidenav.js +583 -583
  33. package/js/slider.js +359 -359
  34. package/js/tabs.js +402 -402
  35. package/js/tapTarget.js +315 -315
  36. package/js/timepicker.js +712 -648
  37. package/js/toasts.js +325 -322
  38. package/js/tooltip.js +320 -320
  39. package/js/waves.js +614 -614
  40. package/package.json +87 -82
  41. package/sass/_style.scss +929 -929
  42. package/sass/components/_badges.scss +55 -55
  43. package/sass/components/_buttons.scss +322 -322
  44. package/sass/components/_cards.scss +195 -195
  45. package/sass/components/_carousel.scss +90 -90
  46. package/sass/components/_chips.scss +96 -96
  47. package/sass/components/_collapsible.scss +91 -91
  48. package/sass/components/_collection.scss +106 -106
  49. package/sass/components/_color-classes.scss +32 -32
  50. package/sass/components/_color-variables.scss +370 -370
  51. package/sass/components/_datepicker.scss +191 -191
  52. package/sass/components/_dropdown.scss +84 -84
  53. package/sass/components/_global.scss +646 -642
  54. package/sass/components/_grid.scss +158 -158
  55. package/sass/components/_icons-material-design.scss +5 -5
  56. package/sass/components/_materialbox.scss +42 -42
  57. package/sass/components/_modal.scss +97 -97
  58. package/sass/components/_navbar.scss +208 -208
  59. package/sass/components/_normalize.scss +447 -447
  60. package/sass/components/_preloader.scss +334 -334
  61. package/sass/components/_pulse.scss +34 -34
  62. package/sass/components/_sidenav.scss +214 -214
  63. package/sass/components/_slider.scss +91 -91
  64. package/sass/components/_table_of_contents.scss +33 -33
  65. package/sass/components/_tabs.scss +99 -99
  66. package/sass/components/_tapTarget.scss +103 -103
  67. package/sass/components/_timepicker.scss +199 -183
  68. package/sass/components/_toast.scss +58 -58
  69. package/sass/components/_tooltip.scss +32 -32
  70. package/sass/components/_transitions.scss +12 -12
  71. package/sass/components/_typography.scss +62 -62
  72. package/sass/components/_variables.scss +352 -352
  73. package/sass/components/_waves.scss +187 -187
  74. package/sass/components/forms/_checkboxes.scss +200 -200
  75. package/sass/components/forms/_file-input.scss +44 -44
  76. package/sass/components/forms/_forms.scss +22 -22
  77. package/sass/components/forms/_input-fields.scss +388 -379
  78. package/sass/components/forms/_radio-buttons.scss +115 -115
  79. package/sass/components/forms/_range.scss +161 -161
  80. package/sass/components/forms/_select.scss +199 -199
  81. package/sass/components/forms/_switches.scss +91 -91
  82. package/sass/ghpages-materialize.scss +7 -7
  83. package/sass/materialize.scss +42 -42
package/js/select.js CHANGED
@@ -1,310 +1,391 @@
1
- (function($) {
2
- 'use strict';
3
-
4
- let _defaults = {
5
- classes: '',
6
- dropdownOptions: {}
7
- };
8
-
9
- class FormSelect extends Component {
10
- constructor(el, options) {
11
- super(FormSelect, el, options);
12
- if (this.$el.hasClass('browser-default')) return;
13
- this.el.M_FormSelect = this;
14
- this.options = $.extend({}, FormSelect.defaults, options);
15
- this.isMultiple = this.$el.prop('multiple');
16
- this.el.tabIndex = -1;
17
- this._values = [];
18
- this._setupDropdown();
19
- this._setupEventHandlers();
20
- }
21
- static get defaults() {
22
- return _defaults;
23
- }
24
- static init(els, options) {
25
- return super.init(this, els, options);
26
- }
27
- static getInstance(el) {
28
- let domElem = !!el.jquery ? el[0] : el;
29
- return domElem.M_FormSelect;
30
- }
31
- destroy() {
32
- this._removeEventHandlers();
33
- this._removeDropdown();
34
- this.el.M_FormSelect = undefined;
35
- }
36
- _setupEventHandlers() {
37
- this._handleSelectChangeBound = this._handleSelectChange.bind(this);
38
- this._handleOptionClickBound = this._handleOptionClick.bind(this);
39
- this._handleInputClickBound = this._handleInputClick.bind(this);
40
- $(this.dropdownOptions)
41
- .find('li:not(.optgroup)')
42
- .each((el) => {
43
- el.addEventListener('click', this._handleOptionClickBound);
44
- });
45
- this.el.addEventListener('change', this._handleSelectChangeBound);
46
- this.input.addEventListener('click', this._handleInputClickBound);
47
- }
48
- _removeEventHandlers() {
49
- $(this.dropdownOptions)
50
- .find('li:not(.optgroup)')
51
- .each((el) => {
52
- el.removeEventListener('click', this._handleOptionClickBound);
53
- });
54
- this.el.removeEventListener('change', this._handleSelectChangeBound);
55
- this.input.removeEventListener('click', this._handleInputClickBound);
56
- }
57
- _handleSelectChange(e) {
58
- this._setValueToInput();
59
- }
60
- _handleOptionClick(e) {
61
- e.preventDefault();
62
- let virtualOption = $(e.target).closest('li')[0];
63
- this._selectOptionElement(virtualOption);
64
- e.stopPropagation();
65
- }
66
- _arraysEqual(a, b) {
67
- if (a === b) return true;
68
- if (a == null || b == null) return false;
69
- if (a.length !== b.length) return false;
70
- for (let i = 0; i < a.length; ++i) if (a[i] !== b[i]) return false;
71
- return true;
72
- }
73
- _selectOptionElement(virtualOption) {
74
- if (!$(virtualOption).hasClass('disabled') && !$(virtualOption).hasClass('optgroup')) {
75
- const value = this._values.filter((value) => value.optionEl === virtualOption)[0];
76
- const previousSelectedValues = this.getSelectedValues();
77
- if (this.isMultiple) {
78
- // Multi-Select
79
- this._toggleEntryFromArray(value);
80
- } else {
81
- // Single-Select
82
- this._deselectAll();
83
- this._selectValue(value);
84
- }
85
- // Refresh Input-Text
86
- this._setValueToInput();
87
- // Trigger Change-Event only when data is different
88
- const actualSelectedValues = this.getSelectedValues();
89
- const selectionHasChanged = !this._arraysEqual(
90
- previousSelectedValues,
91
- actualSelectedValues
92
- );
93
- if (selectionHasChanged) this.$el.trigger('change');
94
- }
95
- if (!this.isMultiple) this.dropdown.close();
96
- }
97
- _handleInputClick() {
98
- if (this.dropdown && this.dropdown.isOpen) {
99
- this._setValueToInput();
100
- this._setSelectedStates();
101
- }
102
- }
103
- _setupDropdown() {
104
- this.wrapper = document.createElement('div');
105
- $(this.wrapper).addClass('select-wrapper ' + this.options.classes);
106
- this.$el.before($(this.wrapper));
107
-
108
- // Move actual select element into overflow hidden wrapper
109
- let $hideSelect = $('<div class="hide-select"></div>');
110
- $(this.wrapper).append($hideSelect);
111
- $hideSelect[0].appendChild(this.el);
112
-
113
- if (this.el.disabled) this.wrapper.classList.add('disabled');
114
-
115
- // Create dropdown
116
- this.$selectOptions = this.$el.children('option, optgroup');
117
- this.dropdownOptions = document.createElement('ul');
118
- this.dropdownOptions.id = `select-options-${M.guid()}`;
119
- $(this.dropdownOptions).addClass(
120
- 'dropdown-content select-dropdown ' + (this.isMultiple ? 'multiple-select-dropdown' : '')
121
- );
122
-
123
- // Create dropdown structure
124
- if (this.$selectOptions.length) {
125
- this.$selectOptions.each((realOption) => {
126
- if ($(realOption).is('option')) {
127
- // Option
128
- const virtualOption = this._createAndAppendOptionWithIcon(
129
- realOption,
130
- this.isMultiple ? 'multiple' : undefined
131
- );
132
- this._addOptionToValues(realOption, virtualOption);
133
- } else if ($(realOption).is('optgroup')) {
134
- // Optgroup
135
- const selectOptions = $(realOption).children('option');
136
- $(this.dropdownOptions).append(
137
- $(
138
- '<li class="optgroup"><span>' + realOption.getAttribute('label') + '</span></li>'
139
- )[0]
140
- );
141
- selectOptions.each((realOption) => {
142
- const virtualOption = this._createAndAppendOptionWithIcon(
143
- realOption,
144
- 'optgroup-option'
145
- );
146
- this._addOptionToValues(realOption, virtualOption);
147
- });
148
- }
149
- });
150
- }
151
- $(this.wrapper).append(this.dropdownOptions);
152
-
153
- // Add input dropdown
154
- this.input = document.createElement('input');
155
- $(this.input).addClass('select-dropdown dropdown-trigger');
156
- this.input.setAttribute('type', 'text');
157
- this.input.setAttribute('readonly', 'true');
158
- this.input.setAttribute('data-target', this.dropdownOptions.id);
159
- if (this.el.disabled) $(this.input).prop('disabled', 'true');
160
-
161
- $(this.wrapper).prepend(this.input);
162
- this._setValueToInput();
163
-
164
- // Add caret
165
- let dropdownIcon = $(
166
- '<svg class="caret" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'
167
- );
168
- $(this.wrapper).prepend(dropdownIcon[0]);
169
- // Initialize dropdown
170
- if (!this.el.disabled) {
171
- let dropdownOptions = $.extend({}, this.options.dropdownOptions);
172
- dropdownOptions.coverTrigger = false;
173
- let userOnOpenEnd = dropdownOptions.onOpenEnd;
174
- // Add callback for centering selected option when dropdown content is scrollable
175
- dropdownOptions.onOpenEnd = (el) => {
176
- let selectedOption = $(this.dropdownOptions)
177
- .find('.selected')
178
- .first();
179
- if (selectedOption.length) {
180
- // Focus selected option in dropdown
181
- M.keyDown = true;
182
- this.dropdown.focusedIndex = selectedOption.index();
183
- this.dropdown._focusFocusedItem();
184
- M.keyDown = false;
185
- // Handle scrolling to selected option
186
- if (this.dropdown.isScrollable) {
187
- let scrollOffset =
188
- selectedOption[0].getBoundingClientRect().top -
189
- this.dropdownOptions.getBoundingClientRect().top; // scroll to selected option
190
- scrollOffset -= this.dropdownOptions.clientHeight / 2; // center in dropdown
191
- this.dropdownOptions.scrollTop = scrollOffset;
192
- }
193
- }
194
- // Handle user declared onOpenEnd if needed
195
- if (userOnOpenEnd && typeof userOnOpenEnd === 'function')
196
- userOnOpenEnd.call(this.dropdown, this.el);
197
- };
198
- // Prevent dropdown from closing too early
199
- dropdownOptions.closeOnClick = false;
200
- this.dropdown = M.Dropdown.init(this.input, dropdownOptions);
201
- }
202
- // Add initial selections
203
- this._setSelectedStates();
204
- }
205
- _addOptionToValues(realOption, virtualOption) {
206
- this._values.push({ el: realOption, optionEl: virtualOption });
207
- }
208
- _removeDropdown() {
209
- $(this.wrapper)
210
- .find('.caret')
211
- .remove();
212
- $(this.input).remove();
213
- $(this.dropdownOptions).remove();
214
- $(this.wrapper).before(this.$el);
215
- $(this.wrapper).remove();
216
- }
217
- _createAndAppendOptionWithIcon(realOption, type) {
218
- const li = document.createElement('li');
219
- if (realOption.disabled) li.classList.add('disabled');
220
- if (type === 'optgroup-option') li.classList.add(type);
221
- // Text / Checkbox
222
- const span = document.createElement('span');
223
- if (this.isMultiple)
224
- span.innerHTML = `<label><input type="checkbox"${
225
- realOption.disabled ? ' disabled="disabled"' : ''
226
- }><span>${realOption.innerHTML}</span></label>`;
227
- else span.innerHTML = realOption.innerHTML;
228
- li.appendChild(span);
229
- // add Icon
230
- const iconUrl = realOption.getAttribute('data-icon');
231
- const classes = realOption.getAttribute('class');
232
- if (iconUrl) {
233
- const img = $(`<img alt="" class="${classes}" src="${iconUrl}">`);
234
- li.prepend(img[0]);
235
- }
236
- // Check for multiple type
237
- $(this.dropdownOptions).append(li);
238
- return li;
239
- }
240
-
241
- _selectValue(value) {
242
- value.el.selected = true;
243
- value.optionEl.classList.add('selected');
244
- const checkbox = value.optionEl.querySelector('input[type="checkbox"]');
245
- if (checkbox) checkbox.checked = true;
246
- }
247
- _deselectValue(value) {
248
- value.el.selected = false;
249
- value.optionEl.classList.remove('selected');
250
- const checkbox = value.optionEl.querySelector('input[type="checkbox"]');
251
- if (checkbox) checkbox.checked = false;
252
- }
253
- _deselectAll() {
254
- this._values.forEach((value) => {
255
- this._deselectValue(value);
256
- });
257
- }
258
- _isValueSelected(value) {
259
- const realValues = this.getSelectedValues();
260
- return realValues.some((realValue) => realValue === value.el.value);
261
- }
262
- _toggleEntryFromArray(value) {
263
- const isSelected = this._isValueSelected(value);
264
- if (isSelected) this._deselectValue(value);
265
- else this._selectValue(value);
266
- }
267
- _getSelectedOptions() {
268
- return Array.prototype.filter.call(this.el.selectedOptions, (realOption) => realOption);
269
- }
270
-
271
- _setValueToInput() {
272
- const realOptions = this._getSelectedOptions();
273
- const values = this._values.filter((value) => realOptions.indexOf(value.el) >= 0);
274
- const texts = values.map((value) => value.optionEl.querySelector('span').innerText.trim());
275
- // Set input-text to first Option with empty value which indicates a description like "choose your option"
276
- if (texts.length === 0) {
277
- const firstDisabledOption = this.$el.find('option:disabled').eq(0);
278
- if (firstDisabledOption.length > 0 && firstDisabledOption[0].value === '') {
279
- this.input.value = firstDisabledOption.text();
280
- return;
281
- }
282
- }
283
- this.input.value = texts.join(', ');
284
- }
285
- _setSelectedStates() {
286
- this._values.forEach((value) => {
287
- const optionIsSelected = $(value.el).prop('selected');
288
- $(value.optionEl)
289
- .find('input[type="checkbox"]')
290
- .prop('checked', optionIsSelected);
291
- if (optionIsSelected) {
292
- this._activateOption($(this.dropdownOptions), $(value.optionEl));
293
- } else $(value.optionEl).removeClass('selected');
294
- });
295
- }
296
- _activateOption(ul, li) {
297
- if (!li) return;
298
- if (!this.isMultiple) ul.find('li.selected').removeClass('selected');
299
- $(li).addClass('selected');
300
- }
301
-
302
- getSelectedValues() {
303
- return this._getSelectedOptions().map((realOption) => realOption.value);
304
- }
305
- }
306
-
307
- M.FormSelect = FormSelect;
308
-
309
- if (M.jQueryLoaded) M.initializeJqueryWrapper(FormSelect, 'formSelect', 'M_FormSelect');
310
- })(cash);
1
+ (function($) {
2
+ 'use strict';
3
+
4
+ let _defaults = {
5
+ classes: '',
6
+ dropdownOptions: {}
7
+ };
8
+
9
+ class FormSelect extends Component {
10
+ constructor(el, options) {
11
+ super(FormSelect, el, options);
12
+ if (this.$el.hasClass('browser-default')) return;
13
+ this.el.M_FormSelect = this;
14
+ this.options = $.extend({}, FormSelect.defaults, options);
15
+ this.isMultiple = this.$el.prop('multiple');
16
+ this.el.tabIndex = -1;
17
+ this._values = [];
18
+ this.labelEl = null;
19
+ this._labelFor = false;
20
+ this._setupDropdown();
21
+ this._setupEventHandlers();
22
+ }
23
+ static get defaults() {
24
+ return _defaults;
25
+ }
26
+ static init(els, options) {
27
+ return super.init(this, els, options);
28
+ }
29
+ static getInstance(el) {
30
+ let domElem = !!el.jquery ? el[0] : el;
31
+ return domElem.M_FormSelect;
32
+ }
33
+ destroy() {
34
+ // Returns label to its original owner
35
+ if (this._labelFor) this.labelEl.setAttribute("for", this.el.id);
36
+ this._removeEventHandlers();
37
+ this._removeDropdown();
38
+ this.el.M_FormSelect = undefined;
39
+ }
40
+ _setupEventHandlers() {
41
+ this._handleSelectChangeBound = this._handleSelectChange.bind(this);
42
+ this._handleOptionClickBound = this._handleOptionClick.bind(this);
43
+ this._handleInputClickBound = this._handleInputClick.bind(this);
44
+ $(this.dropdownOptions)
45
+ .find('li:not(.optgroup)')
46
+ .each((el) => {
47
+ el.addEventListener('click', this._handleOptionClickBound);
48
+ el.addEventListener('keydown', (e) => {
49
+ if (e.key === " " || e.key === "Enter") this._handleOptionClickBound(e);
50
+ });
51
+ });
52
+ this.el.addEventListener('change', this._handleSelectChangeBound);
53
+ this.input.addEventListener('click', this._handleInputClickBound);
54
+ }
55
+ _removeEventHandlers() {
56
+ $(this.dropdownOptions)
57
+ .find('li:not(.optgroup)')
58
+ .each((el) => {
59
+ el.removeEventListener('click', this._handleOptionClickBound);
60
+ });
61
+ this.el.removeEventListener('change', this._handleSelectChangeBound);
62
+ this.input.removeEventListener('click', this._handleInputClickBound);
63
+ }
64
+ _handleSelectChange(e) {
65
+ this._setValueToInput();
66
+ }
67
+ _handleOptionClick(e) {
68
+ e.preventDefault();
69
+ let virtualOption = $(e.target).closest('li')[0];
70
+ this._selectOptionElement(virtualOption);
71
+ e.stopPropagation();
72
+ }
73
+ _arraysEqual(a, b) {
74
+ if (a === b) return true;
75
+ if (a == null || b == null) return false;
76
+ if (a.length !== b.length) return false;
77
+ for (let i = 0; i < a.length; ++i) if (a[i] !== b[i]) return false;
78
+ return true;
79
+ }
80
+ _selectOptionElement(virtualOption) {
81
+ if (!$(virtualOption).hasClass('disabled') && !$(virtualOption).hasClass('optgroup')) {
82
+ const value = this._values.filter((value) => value.optionEl === virtualOption)[0];
83
+ const previousSelectedValues = this.getSelectedValues();
84
+ if (this.isMultiple) {
85
+ // Multi-Select
86
+ this._toggleEntryFromArray(value);
87
+ } else {
88
+ // Single-Select
89
+ this._deselectAll();
90
+ this._selectValue(value);
91
+ }
92
+ // Refresh Input-Text
93
+ this._setValueToInput();
94
+ // Trigger Change-Event only when data is different
95
+ const actualSelectedValues = this.getSelectedValues();
96
+ const selectionHasChanged = !this._arraysEqual(
97
+ previousSelectedValues,
98
+ actualSelectedValues
99
+ );
100
+ if (selectionHasChanged) this.$el.trigger('change');
101
+ }
102
+ if (!this.isMultiple) this.dropdown.close();
103
+ }
104
+ _handleInputClick() {
105
+ if (this.dropdown && this.dropdown.isOpen) {
106
+ this._setValueToInput();
107
+ this._setSelectedStates();
108
+ }
109
+ }
110
+ _setupDropdown() {
111
+ this.wrapper = document.createElement('div');
112
+ $(this.wrapper).addClass('select-wrapper ' + this.options.classes);
113
+ this.$el.before($(this.wrapper));
114
+
115
+ // Move actual select element into overflow hidden wrapper
116
+ let $hideSelect = $('<div class="hide-select"></div>');
117
+ $(this.wrapper).append($hideSelect);
118
+ $hideSelect[0].appendChild(this.el);
119
+
120
+ if (this.el.disabled) this.wrapper.classList.add('disabled');
121
+
122
+ // Create dropdown
123
+ this.$selectOptions = this.$el.children('option, optgroup');
124
+ this.dropdownOptions = document.createElement('ul');
125
+ this.dropdownOptions.id = `select-options-${M.guid()}`;
126
+ $(this.dropdownOptions).addClass(
127
+ 'dropdown-content select-dropdown ' + (this.isMultiple ? 'multiple-select-dropdown' : '')
128
+ );
129
+ this.dropdownOptions.setAttribute("role", "listbox");
130
+ this.dropdownOptions.setAttribute("aria-multiselectable", this.isMultiple);
131
+
132
+ // Create dropdown structure
133
+ if (this.$selectOptions.length) {
134
+ this.$selectOptions.each((realOption) => {
135
+ if ($(realOption).is('option')) {
136
+ // Option
137
+ const virtualOption = this._createAndAppendOptionWithIcon(
138
+ realOption,
139
+ this.isMultiple ? 'multiple' : undefined
140
+ );
141
+ this._addOptionToValues(realOption, virtualOption);
142
+ } else if ($(realOption).is('optgroup')) {
143
+ // Optgroup
144
+ const selectOptions = $(realOption).children('option');
145
+ let lId = "opt-group-" + M.guid();
146
+ let groupParent = $(
147
+ `<li class="optgroup" role="group" aria-labelledby="${lId}" tabindex="-1"><span id="${lId}" role="presentation">${realOption.getAttribute('label')}</span></li>`
148
+ )[0];
149
+ let groupChildren = [];
150
+ $(this.dropdownOptions).append(groupParent);
151
+ selectOptions.each((realOption) => {
152
+ const virtualOption = this._createAndAppendOptionWithIcon(
153
+ realOption,
154
+ 'optgroup-option'
155
+ );
156
+ let cId = "opt-child-" + M.guid();
157
+ virtualOption.id = cId;
158
+ groupChildren.push(cId);
159
+ this._addOptionToValues(realOption, virtualOption);
160
+ });
161
+ groupParent.setAttribute("aria-owns", groupChildren.join(" "));
162
+ }
163
+ });
164
+ }
165
+ $(this.wrapper).append(this.dropdownOptions);
166
+
167
+ // Add input dropdown
168
+ this.input = document.createElement('input');
169
+ this.input.id = "m_select-input-" + M.guid();
170
+ $(this.input).addClass('select-dropdown dropdown-trigger');
171
+ this.input.setAttribute('type', 'text');
172
+ this.input.setAttribute('readonly', 'true');
173
+ this.input.setAttribute('data-target', this.dropdownOptions.id);
174
+ this.input.setAttribute('aria-readonly', 'true');
175
+ this.input.setAttribute("aria-required", this.el.hasAttribute("required"));
176
+ if (this.el.disabled) $(this.input).prop('disabled', 'true');
177
+
178
+ // Makes new element to assume HTML's select label and
179
+ // aria-attributes, if exists
180
+ if (this.el.hasAttribute("aria-labelledby")){
181
+ this.labelEl = document.getElementById(this.el.getAttribute("aria-labelledby"));
182
+ }
183
+ else if (this.el.id != ""){
184
+ let lbl = $(`label[for='${this.el.id}']`);
185
+ if (lbl.length){
186
+ this.labelEl = lbl[0];
187
+ this.labelEl.removeAttribute("for");
188
+ this._labelFor = true;
189
+ }
190
+ }
191
+ // Tries to find a valid label in parent element
192
+ if (!this.labelEl){
193
+ let el = this.el.parentElement;
194
+ if (el) el = el.getElementsByTagName("label")[0];
195
+ if (el) this.labelEl = el;
196
+ }
197
+ if (this.labelEl && this.labelEl.id == ""){
198
+ this.labelEl.id = "m_select-label-" + M.guid();
199
+ }
200
+
201
+ if (this.labelEl){
202
+ this.labelEl.setAttribute("for", this.input.id);
203
+ this.dropdownOptions.setAttribute("aria-labelledby", this.labelEl.id);
204
+ }
205
+ else this.dropdownOptions.setAttribute("aria-label", "");
206
+
207
+ let attrs = this.el.attributes;
208
+ for (let i = 0; i < attrs.length; ++i){
209
+ const attr = attrs[i];
210
+ if (attr.name.startsWith("aria-"))
211
+ this.input.setAttribute(attr.name, attr.value);
212
+ }
213
+
214
+ // Adds aria-attributes to input element
215
+ this.input.setAttribute("role", "combobox");
216
+ this.input.setAttribute("aria-owns", this.dropdownOptions.id);
217
+ this.input.setAttribute("aria-controls", this.dropdownOptions.id);
218
+ this.input.setAttribute("aria-expanded", false);
219
+
220
+ $(this.wrapper).prepend(this.input);
221
+ this._setValueToInput();
222
+
223
+ // Add caret
224
+ let dropdownIcon = $(
225
+ '<svg class="caret" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M7 10l5 5 5-5z"/><path d="M0 0h24v24H0z" fill="none"/></svg>'
226
+ );
227
+ $(this.wrapper).prepend(dropdownIcon[0]);
228
+ // Initialize dropdown
229
+ if (!this.el.disabled) {
230
+ let dropdownOptions = $.extend({}, this.options.dropdownOptions);
231
+ dropdownOptions.coverTrigger = false;
232
+ let userOnOpenEnd = dropdownOptions.onOpenEnd;
233
+ let userOnCloseEnd = dropdownOptions.onCloseEnd;
234
+ // Add callback for centering selected option when dropdown content is scrollable
235
+ dropdownOptions.onOpenEnd = (el) => {
236
+ let selectedOption = $(this.dropdownOptions)
237
+ .find('.selected')
238
+ .first();
239
+ if (selectedOption.length) {
240
+ // Focus selected option in dropdown
241
+ M.keyDown = true;
242
+ this.dropdown.focusedIndex = selectedOption.index();
243
+ this.dropdown._focusFocusedItem();
244
+ M.keyDown = false;
245
+ // Handle scrolling to selected option
246
+ if (this.dropdown.isScrollable) {
247
+ let scrollOffset =
248
+ selectedOption[0].getBoundingClientRect().top -
249
+ this.dropdownOptions.getBoundingClientRect().top; // scroll to selected option
250
+ scrollOffset -= this.dropdownOptions.clientHeight / 2; // center in dropdown
251
+ this.dropdownOptions.scrollTop = scrollOffset;
252
+ }
253
+ }
254
+ // Sets "aria-expanded" to "true"
255
+ this.input.setAttribute("aria-expanded", true);
256
+ // Handle user declared onOpenEnd if needed
257
+ if (userOnOpenEnd && typeof userOnOpenEnd === 'function')
258
+ userOnOpenEnd.call(this.dropdown, this.el);
259
+ };
260
+ // Add callback for reseting "expanded" state
261
+ dropdownOptions.onCloseEnd = (el) => {
262
+ // Sets "aria-expanded" to "false"
263
+ this.input.setAttribute("aria-expanded", false);
264
+ // Handle user declared onOpenEnd if needed
265
+ if (userOnCloseEnd && typeof userOnCloseEnd === 'function')
266
+ userOnCloseEnd.call(this.dropdown, this.el);
267
+ };
268
+ // Prevent dropdown from closing too early
269
+ dropdownOptions.closeOnClick = false;
270
+ this.dropdown = M.Dropdown.init(this.input, dropdownOptions);
271
+ }
272
+ // Add initial selections
273
+ this._setSelectedStates();
274
+ }
275
+ _addOptionToValues(realOption, virtualOption) {
276
+ this._values.push({ el: realOption, optionEl: virtualOption });
277
+ }
278
+ _removeDropdown() {
279
+ $(this.wrapper)
280
+ .find('.caret')
281
+ .remove();
282
+ $(this.input).remove();
283
+ $(this.dropdownOptions).remove();
284
+ $(this.wrapper).before(this.$el);
285
+ $(this.wrapper).remove();
286
+ }
287
+ _createAndAppendOptionWithIcon(realOption, type) {
288
+ const li = document.createElement('li');
289
+ li.setAttribute("role", "option");
290
+ if (realOption.disabled){
291
+ li.classList.add('disabled');
292
+ li.setAttribute("aria-disabled", true);
293
+ }
294
+ if (type === 'optgroup-option') li.classList.add(type);
295
+ // Text / Checkbox
296
+ const span = document.createElement('span');
297
+ if (this.isMultiple)
298
+ span.innerHTML = `<label><input type="checkbox"${
299
+ realOption.disabled ? ' disabled="disabled"' : ''
300
+ }><span>${realOption.innerHTML}</span></label>`;
301
+ else span.innerHTML = realOption.innerHTML;
302
+ li.appendChild(span);
303
+ // add Icon
304
+ const iconUrl = realOption.getAttribute('data-icon');
305
+ const classes = realOption.getAttribute('class');
306
+ if (iconUrl) {
307
+ const img = $(`<img alt="" class="${classes}" src="${iconUrl}">`);
308
+ img[0].setAttribute("aria-hidden", true);
309
+ li.prepend(img[0]);
310
+ }
311
+ // Check for multiple type
312
+ $(this.dropdownOptions).append(li);
313
+ return li;
314
+ }
315
+
316
+ _selectValue(value) {
317
+ value.el.selected = true;
318
+ value.optionEl.classList.add('selected');
319
+ value.optionEl.setAttribute("aria-selected", true);
320
+ const checkbox = value.optionEl.querySelector('input[type="checkbox"]');
321
+ if (checkbox) checkbox.checked = true;
322
+ }
323
+ _deselectValue(value) {
324
+ value.el.selected = false;
325
+ value.optionEl.classList.remove('selected');
326
+ value.optionEl.setAttribute("aria-selected", false);
327
+ const checkbox = value.optionEl.querySelector('input[type="checkbox"]');
328
+ if (checkbox) checkbox.checked = false;
329
+ }
330
+ _deselectAll() {
331
+ this._values.forEach((value) => {
332
+ this._deselectValue(value);
333
+ });
334
+ }
335
+ _isValueSelected(value) {
336
+ const realValues = this.getSelectedValues();
337
+ return realValues.some((realValue) => realValue === value.el.value);
338
+ }
339
+ _toggleEntryFromArray(value) {
340
+ const isSelected = this._isValueSelected(value);
341
+ if (isSelected) this._deselectValue(value);
342
+ else this._selectValue(value);
343
+ }
344
+ _getSelectedOptions() {
345
+ return Array.prototype.filter.call(this.el.selectedOptions, (realOption) => realOption);
346
+ }
347
+
348
+ _setValueToInput() {
349
+ const realOptions = this._getSelectedOptions();
350
+ const values = this._values.filter((value) => realOptions.indexOf(value.el) >= 0);
351
+ const texts = values.map((value) => value.optionEl.querySelector('span').innerText.trim());
352
+ // Set input-text to first Option with empty value which indicates a description like "choose your option"
353
+ if (texts.length === 0) {
354
+ const firstDisabledOption = this.$el.find('option:disabled').eq(0);
355
+ if (firstDisabledOption.length > 0 && firstDisabledOption[0].value === '') {
356
+ this.input.value = firstDisabledOption.text();
357
+ return;
358
+ }
359
+ }
360
+ this.input.value = texts.join(', ');
361
+ }
362
+ _setSelectedStates() {
363
+ this._values.forEach((value) => {
364
+ const optionIsSelected = $(value.el).prop('selected');
365
+ $(value.optionEl)
366
+ .find('input[type="checkbox"]')
367
+ .prop('checked', optionIsSelected);
368
+ if (optionIsSelected) {
369
+ this._activateOption($(this.dropdownOptions), $(value.optionEl));
370
+ } else {
371
+ $(value.optionEl).removeClass('selected');
372
+ $(value.optionEl).attr("aria-selected", false);
373
+ }
374
+ });
375
+ }
376
+ _activateOption(ul, li) {
377
+ if (!li) return;
378
+ if (!this.isMultiple) ul.find('li.selected').removeClass('selected');
379
+ $(li).addClass('selected');
380
+ $(li).attr("aria-selected", true);
381
+ }
382
+
383
+ getSelectedValues() {
384
+ return this._getSelectedOptions().map((realOption) => realOption.value);
385
+ }
386
+ }
387
+
388
+ M.FormSelect = FormSelect;
389
+
390
+ if (M.jQueryLoaded) M.initializeJqueryWrapper(FormSelect, 'formSelect', 'M_FormSelect');
391
+ })(cash);