@materializecss/materialize 1.2.2 → 2.0.0-alpha

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 (89) hide show
  1. package/Gruntfile.js +68 -313
  2. package/README.md +2 -2
  3. package/dist/css/materialize.css +1009 -1822
  4. package/dist/css/materialize.min.css +2 -8
  5. package/dist/js/materialize.js +8402 -12300
  6. package/dist/js/materialize.min.js +3 -2
  7. package/dist/js/materialize.min.js.map +1 -0
  8. package/package.json +13 -9
  9. package/sass/components/_badges.scss +12 -2
  10. package/sass/components/_buttons.scss +16 -11
  11. package/sass/components/_cards.scss +14 -9
  12. package/sass/components/_carousel.scss +5 -2
  13. package/sass/components/_chips.scss +3 -3
  14. package/sass/components/_collapsible.scss +22 -8
  15. package/sass/components/_collection.scss +14 -6
  16. package/sass/components/_datepicker.scss +30 -11
  17. package/sass/components/_dropdown.scss +6 -4
  18. package/sass/components/_global.scss +132 -111
  19. package/sass/components/_grid.scss +119 -98
  20. package/sass/components/_modal.scss +3 -3
  21. package/sass/components/_navbar.scss +31 -17
  22. package/sass/components/_normalize.scss +26 -124
  23. package/sass/components/_sidenav.scss +21 -20
  24. package/sass/components/_slider.scss +27 -7
  25. package/sass/components/_table_of_contents.scss +12 -12
  26. package/sass/components/_tabs.scss +47 -16
  27. package/sass/components/_tapTarget.scss +6 -6
  28. package/sass/components/_timepicker.scss +54 -46
  29. package/sass/components/_toast.scss +3 -3
  30. package/sass/components/_tooltip.scss +4 -5
  31. package/sass/components/_typography.scss +1 -1
  32. package/sass/components/_variables.scss +185 -120
  33. package/sass/components/forms/_checkboxes.scss +9 -9
  34. package/sass/components/forms/_file-input.scss +9 -7
  35. package/sass/components/forms/_input-fields.scss +173 -234
  36. package/sass/components/forms/_radio-buttons.scss +1 -1
  37. package/sass/components/forms/_range.scss +11 -11
  38. package/sass/components/forms/_select.scss +29 -19
  39. package/sass/components/forms/_switches.scss +22 -18
  40. package/sass/materialize.scss +1 -1
  41. package/src/autocomplete.ts +459 -0
  42. package/src/bounding.ts +6 -0
  43. package/{js/buttons.js → src/buttons.ts} +103 -162
  44. package/src/cards.ts +54 -0
  45. package/{js/carousel.js → src/carousel.ts} +137 -262
  46. package/src/characterCounter.ts +88 -0
  47. package/src/chips.ts +350 -0
  48. package/src/collapsible.ts +184 -0
  49. package/{js/component.js → src/component.ts} +6 -19
  50. package/{js/datepicker.js → src/datepicker.ts} +213 -299
  51. package/{js/dropdown.js → src/dropdown.ts} +140 -254
  52. package/src/edges.ts +6 -0
  53. package/src/forms.ts +120 -0
  54. package/src/global.ts +385 -0
  55. package/src/materialbox.ts +348 -0
  56. package/src/modal.ts +256 -0
  57. package/{js/parallax.js → src/parallax.ts} +47 -60
  58. package/{js/pushpin.js → src/pushpin.ts} +19 -47
  59. package/{js/range.js → src/range.ts} +58 -139
  60. package/{js/scrollspy.js → src/scrollspy.ts} +81 -153
  61. package/src/select.ts +448 -0
  62. package/{js/sidenav.js → src/sidenav.ts} +96 -202
  63. package/src/slider.ts +415 -0
  64. package/src/tabs.ts +290 -0
  65. package/src/tapTarget.ts +240 -0
  66. package/{js/timepicker.js → src/timepicker.ts} +268 -272
  67. package/{js/toasts.js → src/toasts.ts} +75 -134
  68. package/{js/tooltip.js → src/tooltip.ts} +59 -96
  69. package/src/waves.ts +70 -0
  70. package/extras/noUiSlider/nouislider.css +0 -404
  71. package/extras/noUiSlider/nouislider.js +0 -2147
  72. package/extras/noUiSlider/nouislider.min.js +0 -1
  73. package/js/anime.min.js +0 -34
  74. package/js/autocomplete.js +0 -479
  75. package/js/cards.js +0 -40
  76. package/js/cash.js +0 -960
  77. package/js/characterCounter.js +0 -136
  78. package/js/chips.js +0 -486
  79. package/js/collapsible.js +0 -275
  80. package/js/forms.js +0 -285
  81. package/js/global.js +0 -428
  82. package/js/materialbox.js +0 -453
  83. package/js/modal.js +0 -382
  84. package/js/select.js +0 -391
  85. package/js/slider.js +0 -497
  86. package/js/tabs.js +0 -402
  87. package/js/tapTarget.js +0 -315
  88. package/js/waves.js +0 -615
  89. package/sass/components/_waves.scss +0 -187
@@ -1,7 +1,11 @@
1
1
  /* Select Field
2
2
  ========================================================================== */
3
3
 
4
- select.browser-default { opacity: 1; }
4
+ select.browser-default {
5
+ opacity: 1;
6
+ color: $select-input-color;
7
+ }
8
+
5
9
  select {
6
10
  opacity: 0;
7
11
  background-color: $select-background;
@@ -12,11 +16,13 @@ select {
12
16
  height: $input-height;
13
17
  }
14
18
 
15
- .select-label {
16
- position: absolute;
17
- }
19
+ // .select-label {
20
+ // position: absolute;
21
+ // }
22
+
18
23
 
19
24
  .select-wrapper {
25
+ /*
20
26
  &.valid .helper-text[data-success],
21
27
  &.invalid ~ .helper-text[data-error] {
22
28
  @extend %hidden-text;
@@ -26,9 +32,8 @@ select {
26
32
  & > input.select-dropdown {
27
33
  @extend %valid-input-style;
28
34
  }
29
-
30
35
  & ~ .helper-text:after {
31
- @extend %custom-success-message;
36
+ //@extend %custom-success-message;
32
37
  }
33
38
  }
34
39
 
@@ -37,9 +42,8 @@ select {
37
42
  & > input.select-dropdown:focus {
38
43
  @extend %invalid-input-style;
39
44
  }
40
-
41
45
  & ~ .helper-text:after {
42
- @extend %custom-error-message;
46
+ //@extend %custom-error-message;
43
47
  }
44
48
  }
45
49
 
@@ -48,14 +52,17 @@ select {
48
52
  width: 100%;
49
53
  pointer-events: none;
50
54
  }
51
-
52
55
  & + label:after {
53
- @extend %input-after-style;
56
+ //@extend %input-after-style;
54
57
  }
58
+ */
59
+
60
+
55
61
 
56
62
  position: relative;
57
63
 
58
- input.select-dropdown {
64
+ /*
65
+ input.select-dropdown {
59
66
  &:focus {
60
67
  border-bottom: 1px solid $input-focus-color;
61
68
  }
@@ -74,7 +81,9 @@ select {
74
81
  display: block;
75
82
  user-select:none;
76
83
  z-index: 1;
84
+ color: $select-input-color;
77
85
  }
86
+ */
78
87
 
79
88
  .caret {
80
89
  position: absolute;
@@ -83,14 +92,16 @@ select {
83
92
  bottom: 0;
84
93
  margin: auto 0;
85
94
  z-index: 0;
86
- fill: rgba(0,0,0,.87);
95
+ fill: $select-input-color;
87
96
  }
88
97
 
98
+ /*
89
99
  & + label {
90
100
  position: absolute;
91
101
  top: -26px;
92
102
  font-size: $label-font-size;
93
103
  }
104
+ */
94
105
 
95
106
  // Hide select with overflow hidden instead of using display none
96
107
  // (this prevents form validation errors with hidden form elements)
@@ -143,33 +154,32 @@ body.keyboard-focused {
143
154
 
144
155
  .select-dropdown.dropdown-content {
145
156
  li {
146
- &:hover {
157
+ &:hover:not(.disabled) {
147
158
  background-color: $select-option-hover;
148
159
  }
149
160
 
150
- &.selected {
161
+ &.selected:not(.disabled) {
151
162
  background-color: $select-option-selected;
152
163
  }
153
164
  }
154
165
  }
155
166
 
167
+ /*
156
168
  // Prefix Icons
157
169
  .prefix ~ .select-wrapper {
158
170
  margin-left: 3rem;
159
171
  width: 92%;
160
172
  width: calc(100% - 3rem);
161
173
  }
162
-
163
174
  .prefix ~ label { margin-left: 3rem; }
164
-
165
175
  // Suffix Icons
166
176
  .suffix ~ .select-wrapper {
167
177
  margin-right: 3rem;
168
178
  width: 92%;
169
179
  width: calc(100% - 3rem);
170
180
  }
171
-
172
181
  .suffix ~ label { margin-right: 3rem; }
182
+ */
173
183
 
174
184
  // Icons
175
185
  .select-dropdown li {
@@ -186,11 +196,11 @@ body.keyboard-focused {
186
196
  border-top: 1px solid $dropdown-hover-bg-color;
187
197
 
188
198
  &.selected > span {
189
- color: rgba(0, 0, 0, .7);
199
+ color: $font-color-main;
190
200
  }
191
201
 
192
202
  & > span {
193
- color: rgba(0, 0, 0, .4);
203
+ color: $font-color-medium;
194
204
  }
195
205
 
196
206
  & ~ li.optgroup-option {
@@ -16,8 +16,8 @@
16
16
  width: 0;
17
17
  height: 0;
18
18
 
19
- &:checked:not([disabled]) {
20
- background-color: $switch-checked-lever-bg;
19
+ &:checked + .lever{
20
+ background-color: $switch-track-checked-bg;
21
21
  }
22
22
 
23
23
  &:checked + .lever {
@@ -26,7 +26,7 @@
26
26
  }
27
27
 
28
28
  &:after {
29
- background-color: $switch-bg-color;
29
+ background-color: $switch-thumb-checked-color;
30
30
  }
31
31
  }
32
32
  }
@@ -37,7 +37,7 @@
37
37
  position: relative;
38
38
  width: 36px;
39
39
  height: 14px;
40
- background-color: $switch-unchecked-lever-bg;
40
+ background-color: $switch-track-unchecked-bg;
41
41
  border-radius: $switch-radius;
42
42
  margin-right: 10px;
43
43
  transition: background 0.3s ease;
@@ -56,36 +56,40 @@
56
56
  transition: left 0.3s ease, background .3s ease, box-shadow 0.1s ease, transform .1s ease;
57
57
  }
58
58
 
59
- &:before {
60
- background-color: transparentize($switch-bg-color, .85);
61
- }
62
-
63
59
  &:after {
64
- background-color: $switch-unchecked-bg;
60
+ background-color: $switch-thumb-unchecked-color;
65
61
  box-shadow: 0px 3px 1px -2px rgba(0, 0, 0, 0.2), 0px 2px 2px 0px rgba(0, 0, 0, 0.14), 0px 1px 5px 0px rgba(0, 0, 0, 0.12);
66
62
  }
67
63
  }
68
64
 
65
+ input[type=checkbox]:not(:disabled) ~ .lever:active:before,
66
+ input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before,
67
+ input[type=checkbox]:not(:disabled) ~ .lever:hover::before {
68
+ transform: scale(2.4);
69
+ }
70
+
71
+ input[type=checkbox]:checked:not(:disabled) ~ .lever:hover::before {
72
+ background-color: $switch-reaction-checked-hover-color;
73
+ }
74
+
69
75
  // Switch active style
70
76
  input[type=checkbox]:checked:not(:disabled) ~ .lever:active::before,
71
77
  input[type=checkbox]:checked:not(:disabled).tabbed:focus ~ .lever::before {
72
- transform: scale(2.4);
73
- background-color: transparentize($switch-bg-color, .85);
78
+ background-color: $switch-reaction-checked-focus-color;
79
+ }
80
+
81
+ input[type=checkbox]:not(:disabled) ~ .lever:hover::before {
82
+ background-color: $switch-reaction-unchecked-hover-color;
74
83
  }
75
84
 
76
85
  input[type=checkbox]:not(:disabled) ~ .lever:active:before,
77
86
  input[type=checkbox]:not(:disabled).tabbed:focus ~ .lever::before {
78
- transform: scale(2.4);
79
- background-color: rgba(0,0,0,.08);
87
+ background-color: $switch-reaction-unchecked-focus-color;
80
88
  }
81
89
 
82
90
  // Disabled Styles
83
91
  .switch input[type=checkbox][disabled] + .lever {
84
92
  cursor: default;
85
- background-color: rgba(0,0,0,.12);
93
+ opacity: 0.5;
86
94
  }
87
95
 
88
- .switch label input[type=checkbox][disabled] + .lever:after,
89
- .switch label input[type=checkbox][disabled]:checked + .lever:after {
90
- background-color: $input-disabled-solid-color;
91
- }
@@ -25,7 +25,6 @@
25
25
  @import "components/tooltip";
26
26
  @import "components/buttons";
27
27
  @import "components/dropdown";
28
- @import "components/waves";
29
28
  @import "components/modal";
30
29
  @import "components/collapsible";
31
30
  @import "components/chips";
@@ -40,3 +39,4 @@
40
39
  @import "components/pulse";
41
40
  @import "components/datepicker";
42
41
  @import "components/timepicker";
42
+ @import "components/_theme_variables";
@@ -0,0 +1,459 @@
1
+ import { Component } from "./component";
2
+ import { M } from "./global";
3
+ import { Dropdown } from "./dropdown";
4
+
5
+ let _defaults = {
6
+ data: [], // Autocomplete data set
7
+ onAutocomplete: null, // Callback for when autocompleted
8
+ dropdownOptions: {
9
+ // Default dropdown options
10
+ autoFocus: false,
11
+ closeOnClick: false,
12
+ coverTrigger: false
13
+ },
14
+ minLength: 1, // Min characters before autocomplete starts
15
+ isMultiSelect: false,
16
+ onSearch: function(text, autocomplete) {
17
+ const filteredData = autocomplete.options.data.filter(item => {
18
+ return Object.keys(item)
19
+ .map(key => item[key].toString().toLowerCase().indexOf(text.toLowerCase()) >= 0)
20
+ .some(isMatch => isMatch);
21
+ });
22
+ autocomplete.setMenuItems(filteredData);
23
+ },
24
+ maxDropDownHeight: '300px',
25
+ allowUnsafeHTML: false
26
+ };
27
+
28
+
29
+ export class Autocomplete extends Component {
30
+ el: HTMLInputElement;
31
+ isOpen: boolean;
32
+ count: number;
33
+ activeIndex: number;
34
+ private oldVal: any;
35
+ private $inputField: any;
36
+ private $active: HTMLElement|null;
37
+ private _mousedown: boolean;
38
+ private _handleInputBlurBound: any;
39
+ private _handleInputKeyupAndFocusBound: any;
40
+ private _handleInputKeydownBound: any;
41
+ private _handleInputClickBound: any;
42
+ private _handleContainerMousedownAndTouchstartBound: any;
43
+ private _handleContainerMouseupAndTouchendBound: any;
44
+ container: HTMLElement;
45
+ dropdown: Dropdown;
46
+ static _keydown: boolean;
47
+ selectedValues: any[];
48
+ menuItems: any[];
49
+
50
+
51
+ constructor(el, options) {
52
+ super(Autocomplete, el, options);
53
+ (this.el as any).M_Autocomplete = this;
54
+ this.options = {...Autocomplete.defaults, ...options};
55
+ this.isOpen = false;
56
+ this.count = 0;
57
+ this.activeIndex = -1;
58
+ this.oldVal;
59
+ this.selectedValues = [];
60
+ this.menuItems = [];
61
+ this.$active = null;
62
+ this._mousedown = false;
63
+ this._setupDropdown();
64
+ this._setupEventHandlers();
65
+ }
66
+ static get defaults() {
67
+ return _defaults;
68
+ }
69
+ static init(els, options) {
70
+ return super.init(this, els, options);
71
+ }
72
+ static getInstance(el) {
73
+ let domElem = el.jquery ? el[0] : el;
74
+ return domElem.M_Autocomplete;
75
+ }
76
+
77
+ destroy() {
78
+ this._removeEventHandlers();
79
+ this._removeDropdown();
80
+ (this.el as any).M_Autocomplete = undefined;
81
+ }
82
+
83
+ _setupEventHandlers() {
84
+ this._handleInputBlurBound = this._handleInputBlur.bind(this);
85
+ this._handleInputKeyupAndFocusBound = this._handleInputKeyupAndFocus.bind(this);
86
+ this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
87
+ this._handleInputClickBound = this._handleInputClick.bind(this);
88
+ this._handleContainerMousedownAndTouchstartBound = this._handleContainerMousedownAndTouchstart.bind(
89
+ this
90
+ );
91
+ this._handleContainerMouseupAndTouchendBound = this._handleContainerMouseupAndTouchend.bind(
92
+ this
93
+ );
94
+ this.el.addEventListener('blur', this._handleInputBlurBound);
95
+ this.el.addEventListener('keyup', this._handleInputKeyupAndFocusBound);
96
+ this.el.addEventListener('focus', this._handleInputKeyupAndFocusBound);
97
+ this.el.addEventListener('keydown', this._handleInputKeydownBound);
98
+ this.el.addEventListener('click', this._handleInputClickBound);
99
+ this.container.addEventListener(
100
+ 'mousedown',
101
+ this._handleContainerMousedownAndTouchstartBound
102
+ );
103
+ this.container.addEventListener('mouseup', this._handleContainerMouseupAndTouchendBound);
104
+ if (typeof window.ontouchstart !== 'undefined') {
105
+ this.container.addEventListener(
106
+ 'touchstart',
107
+ this._handleContainerMousedownAndTouchstartBound
108
+ );
109
+ this.container.addEventListener('touchend', this._handleContainerMouseupAndTouchendBound);
110
+ }
111
+ }
112
+
113
+ _removeEventHandlers() {
114
+ this.el.removeEventListener('blur', this._handleInputBlurBound);
115
+ this.el.removeEventListener('keyup', this._handleInputKeyupAndFocusBound);
116
+ this.el.removeEventListener('focus', this._handleInputKeyupAndFocusBound);
117
+ this.el.removeEventListener('keydown', this._handleInputKeydownBound);
118
+ this.el.removeEventListener('click', this._handleInputClickBound);
119
+ this.container.removeEventListener(
120
+ 'mousedown',
121
+ this._handleContainerMousedownAndTouchstartBound
122
+ );
123
+ this.container.removeEventListener('mouseup', this._handleContainerMouseupAndTouchendBound);
124
+
125
+ if (typeof window.ontouchstart !== 'undefined') {
126
+ this.container.removeEventListener(
127
+ 'touchstart',
128
+ this._handleContainerMousedownAndTouchstartBound
129
+ );
130
+ this.container.removeEventListener(
131
+ 'touchend',
132
+ this._handleContainerMouseupAndTouchendBound
133
+ );
134
+ }
135
+ }
136
+
137
+ _setupDropdown() {
138
+ this.container = document.createElement('ul');
139
+ this.container.style.maxHeight = this.options.maxDropDownHeight;
140
+ this.container.id = `autocomplete-options-${M.guid()}`;
141
+ this.container.classList.add('autocomplete-content', 'dropdown-content');
142
+ this.el.setAttribute('data-target', this.container.id);
143
+
144
+ // ! Issue in Component Dropdown: _placeDropdown moves dom-position
145
+ this.el.parentElement.appendChild(this.container);
146
+
147
+ // Initialize dropdown
148
+ let dropdownOptions = {
149
+ ...Autocomplete.defaults.dropdownOptions,
150
+ ...this.options.dropdownOptions
151
+ };
152
+ let userOnItemClick = dropdownOptions.onItemClick;
153
+ // Ensuring the select Option call when user passes custom onItemClick function to dropdown
154
+ dropdownOptions.onItemClick = (li) => {
155
+ if (!li) return;
156
+ const entryID = li.getAttribute('data-id');
157
+ this.selectOption(entryID);
158
+ // Handle user declared onItemClick if needed
159
+ if (userOnItemClick && typeof userOnItemClick === 'function')
160
+ userOnItemClick.call(this.dropdown, this.el);
161
+ };
162
+ this.dropdown = M.Dropdown.init(this.el, dropdownOptions);
163
+
164
+ // ! Workaround for Label: move label up again
165
+ // TODO: Just use PopperJS in future!
166
+ const label = this.el.parentElement.querySelector('label');
167
+ if (label) this.el.after(label);
168
+
169
+ // Sketchy removal of dropdown click handler
170
+ this.el.removeEventListener('click', this.dropdown._handleClickBound);
171
+ // Set Value if already set in HTML
172
+ if (this.el.value) this.selectOption(this.el.value);
173
+ // Add StatusInfo
174
+ const div = document.createElement('div');
175
+ div.classList.add('status-info');
176
+ div.setAttribute('style', 'position: absolute;right:0;top:0;');
177
+ this.el.parentElement.appendChild(div);
178
+ this._updateSelectedInfo();
179
+ }
180
+
181
+ _removeDropdown() {
182
+ this.container.parentNode.removeChild(this.container);
183
+ }
184
+
185
+ _handleInputBlur() {
186
+ if (!this._mousedown) {
187
+ this.close();
188
+ this._resetAutocomplete();
189
+ }
190
+ }
191
+
192
+ _handleInputKeyupAndFocus(e) {
193
+ if (e.type === 'keyup') Autocomplete._keydown = false;
194
+ this.count = 0;
195
+ const actualValue = this.el.value.toLowerCase();
196
+ // Don't capture enter or arrow key usage.
197
+ if (e.keyCode === 13 || e.keyCode === 38 || e.keyCode === 40) return;
198
+ // Check if the input isn't empty
199
+ // Check if focus triggered by tab
200
+ if (this.oldVal !== actualValue && (M.tabPressed || e.type !== 'focus')) {
201
+ this.open();
202
+ }
203
+ // Value has changed!
204
+ if (this.oldVal !== actualValue) {
205
+ this._setStatusLoading();
206
+ this.options.onSearch(this.el.value, this);
207
+ }
208
+ // Reset Single-Select when Input cleared
209
+ if (!this.options.isMultiSelect && this.el.value.length === 0) {
210
+ this.selectedValues = [];
211
+ this._triggerChanged();
212
+ }
213
+ this.oldVal = actualValue;
214
+ }
215
+
216
+ _handleInputKeydown(e) {
217
+ Autocomplete._keydown = true;
218
+ // Arrow keys and enter key usage
219
+ const keyCode = e.keyCode;
220
+ const numItems = this.container.querySelectorAll('li').length;
221
+ // select element on Enter
222
+ if (keyCode === M.keys.ENTER && this.activeIndex >= 0) {
223
+ const liElement = this.container.querySelectorAll('li')[this.activeIndex];
224
+ if (liElement) {
225
+ this.selectOption(liElement.getAttribute('data-id'));
226
+ e.preventDefault();
227
+ }
228
+ return;
229
+ }
230
+ // Capture up and down key
231
+ if (keyCode === M.keys.ARROW_UP || keyCode === M.keys.ARROW_DOWN) {
232
+ e.preventDefault();
233
+ if (keyCode === M.keys.ARROW_UP && this.activeIndex > 0) this.activeIndex--;
234
+ if (keyCode === M.keys.ARROW_DOWN && this.activeIndex < numItems - 1) this.activeIndex++;
235
+ this.$active?.classList.remove('active');
236
+ if (this.activeIndex >= 0) {
237
+ this.$active = this.container.querySelectorAll('li')[this.activeIndex];
238
+ this.$active?.classList.add('active');
239
+ // Focus selected
240
+ this.container.children[this.activeIndex].scrollIntoView({
241
+ behavior: 'smooth',
242
+ block: 'nearest',
243
+ inline: 'nearest'
244
+ });
245
+ }
246
+ }
247
+ }
248
+
249
+ _handleInputClick(e) {
250
+ this.open();
251
+ }
252
+
253
+ _handleContainerMousedownAndTouchstart(e) {
254
+ this._mousedown = true;
255
+ }
256
+
257
+ _handleContainerMouseupAndTouchend(e) {
258
+ this._mousedown = false;
259
+ }
260
+
261
+ _resetCurrentElementPosition() {
262
+ this.activeIndex = -1;
263
+ this.$active?.classList.remove('active');
264
+ }
265
+
266
+ _resetAutocomplete() {
267
+ this.container.replaceChildren();
268
+ this._resetCurrentElementPosition();
269
+ this.oldVal = null;
270
+ this.isOpen = false;
271
+ this._mousedown = false;
272
+ }
273
+
274
+ _highlightPartialText(input, label) {
275
+ const start = label.toLowerCase().indexOf('' + input.toLowerCase() + '');
276
+ const end = start + input.length - 1;
277
+ //custom filters may return results where the string does not match any part
278
+ if (start == -1 || end == -1) {
279
+ return [label, '', ''];
280
+ }
281
+ return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)];
282
+ }
283
+
284
+ _createDropdownItem(entry) {
285
+ const item = document.createElement('li');
286
+ item.setAttribute('data-id', entry.id);
287
+ item.setAttribute(
288
+ 'style',
289
+ 'display:grid; grid-auto-flow: column; user-select: none; align-items: center;'
290
+ );
291
+ // Checkbox
292
+ if (this.options.isMultiSelect) {
293
+ item.innerHTML = `
294
+ <div class="item-selection" style="text-align:center;">
295
+ <input type="checkbox"${
296
+ this.selectedValues.some((sel) => sel.id === entry.id) ? ' checked="checked"' : ''
297
+ }><span style="padding-left:21px;"></span>
298
+ </div>`;
299
+ }
300
+ // Image
301
+ if (entry.image) {
302
+ const img = document.createElement('img');
303
+ img.classList.add('circle');
304
+ img.src = entry.image;
305
+ item.appendChild(img);
306
+ }
307
+
308
+ // Text
309
+ const inputText = this.el.value.toLowerCase();
310
+ const parts = this._highlightPartialText(inputText, (entry.text || entry.id).toString());
311
+ const div = document.createElement('div');
312
+ div.setAttribute('style', 'line-height:1.2;font-weight:500;');
313
+ if (this.options.allowUnsafeHTML) {
314
+ div.innerHTML = parts[0] + '<span class="highlight">' + parts[1] + '</span>' + parts[2];
315
+ } else {
316
+ div.appendChild(document.createTextNode(parts[0]));
317
+ if (parts[1]) {
318
+ const highlight = document.createElement('span');
319
+ highlight.textContent = parts[1];
320
+ highlight.classList.add('highlight');
321
+ div.appendChild(highlight);
322
+ div.appendChild(document.createTextNode(parts[2]));
323
+ }
324
+ }
325
+
326
+ const itemText = document.createElement('div');
327
+ itemText.classList.add('item-text');
328
+ itemText.setAttribute('style', 'padding:5px;overflow:hidden;');
329
+ item.appendChild(itemText);
330
+ item.querySelector('.item-text').appendChild(div);
331
+ // Description
332
+ if (typeof entry.description === 'string' || (typeof entry.description === 'number' && !isNaN(entry.description))) {
333
+ const description = document.createElement('small');
334
+ description.setAttribute(
335
+ 'style',
336
+ 'line-height:1.3;color:grey;white-space:nowrap;text-overflow:ellipsis;display:block;width:90%;overflow:hidden;'
337
+ );
338
+ description.innerText = entry.description;
339
+ item.querySelector('.item-text').appendChild(description);
340
+ }
341
+ // Set Grid
342
+ const getGridConfig = () => {
343
+ if (this.options.isMultiSelect) {
344
+ if (entry.image) return '40px min-content auto'; // cb-img-txt
345
+ return '40px auto'; // cb-txt
346
+ }
347
+ if (entry.image) return 'min-content auto'; // img-txt
348
+ return 'auto'; // txt
349
+ };
350
+ item.style.gridTemplateColumns = getGridConfig();
351
+ return item;
352
+ }
353
+
354
+ _renderDropdown() {
355
+ this._resetAutocomplete();
356
+ // Check if Data is empty
357
+ if (this.menuItems.length === 0) {
358
+ this.menuItems = this.selectedValues; // Show selected Items
359
+ }
360
+ for (let i = 0; i < this.menuItems.length; i++) {
361
+ const item = this._createDropdownItem(this.menuItems[i]);
362
+ this.container.append(item);
363
+ }
364
+ }
365
+
366
+ _setStatusLoading() {
367
+ this.el.parentElement.querySelector(
368
+ '.status-info'
369
+ ).innerHTML = `<div style="height:100%;width:50px;"><svg version="1.1" id="L4" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 100 100" enable-background="new 0 0 0 0" xml:space="preserve">
370
+ <circle fill="#888c" stroke="none" cx="6" cy="50" r="6"><animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin="0.1"/></circle>
371
+ <circle fill="#888c" stroke="none" cx="26" cy="50" r="6"><animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin="0.2"/></circle>
372
+ <circle fill="#888c" stroke="none" cx="46" cy="50" r="6"><animate attributeName="opacity" dur="1s" values="0;1;0" repeatCount="indefinite" begin="0.3"/></circle>
373
+ </svg></div>`;
374
+ }
375
+
376
+ _updateSelectedInfo() {
377
+ const statusElement = this.el.parentElement.querySelector('.status-info');
378
+ if (statusElement) {
379
+ if (this.options.isMultiSelect) statusElement.innerHTML = this.selectedValues.length.toString();
380
+ else statusElement.innerHTML = '';
381
+ }
382
+ }
383
+
384
+ _refreshInputText() {
385
+ if (this.selectedValues.length === 1) {
386
+ const entry = this.selectedValues[0];
387
+ this.el.value = entry.text || entry.id; // Write Text to Input
388
+ }
389
+ }
390
+
391
+ _triggerChanged() {
392
+ this.el.dispatchEvent(new Event('change'));
393
+ // Trigger Autocomplete Event
394
+ if (typeof this.options.onAutocomplete === 'function')
395
+ this.options.onAutocomplete.call(this, this.selectedValues);
396
+ }
397
+
398
+ open() {
399
+ const inputText = this.el.value.toLowerCase();
400
+ this._resetAutocomplete();
401
+ if (inputText.length >= this.options.minLength) {
402
+ this.isOpen = true;
403
+ this._renderDropdown();
404
+ }
405
+ // Open dropdown
406
+ if (!this.dropdown.isOpen) {
407
+ setTimeout(() => {
408
+ this.dropdown.open();
409
+ }, 100);
410
+ }
411
+ else this.dropdown.recalculateDimensions(); // Recalculate dropdown when its already open
412
+ }
413
+
414
+ close() {
415
+ this.dropdown.close();
416
+ }
417
+
418
+ setMenuItems(menuItems) {
419
+ this.menuItems = menuItems;
420
+ this.open();
421
+ this._updateSelectedInfo();
422
+ }
423
+
424
+ setValues(entries) {
425
+ this.selectedValues = entries;
426
+ this._updateSelectedInfo();
427
+ if (!this.options.isMultiSelect) {
428
+ this._refreshInputText();
429
+ }
430
+ this._triggerChanged();
431
+ }
432
+
433
+ selectOption(id) {
434
+ const entry = this.menuItems.find((item) => item.id == id);
435
+ if (!entry) return;
436
+ // Toggle Checkbox
437
+ const li = this.container.querySelector('li[data-id="'+id+'"]');
438
+ if (!li) return;
439
+ if (this.options.isMultiSelect) {
440
+ const checkbox = <HTMLInputElement|null>li.querySelector('input[type="checkbox"]');
441
+ checkbox.checked = !checkbox.checked;
442
+ if (checkbox.checked) this.selectedValues.push(entry);
443
+ else
444
+ this.selectedValues = this.selectedValues.filter(
445
+ (selectedEntry) => selectedEntry.id !== entry.id
446
+ );
447
+ this.el.focus();
448
+ } else {
449
+ // Single-Select
450
+ this.selectedValues = [entry];
451
+ this._refreshInputText();
452
+ this._resetAutocomplete();
453
+ this.close();
454
+ }
455
+ this._updateSelectedInfo();
456
+ this._triggerChanged();
457
+ }
458
+ }
459
+