kmaterialize 2.0.0 → 2.3.3-1.0.0

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 (150) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +55 -91
  3. package/components/appbar/_navbar.old.scss +223 -0
  4. package/components/appbar/appbar.css +235 -0
  5. package/components/appbar/appbar.mjs +54 -0
  6. package/components/appbar/appbar.test.mjs +9 -0
  7. package/components/atomic/atomic.mjs +19 -0
  8. package/components/atomic/component.mjs +67 -0
  9. package/components/badge/_badges.scss +75 -0
  10. package/components/badge/badge.mjs +11 -0
  11. package/components/breadcrumb/_breadcrumb.scss +27 -0
  12. package/components/button/_buttons.scss +352 -0
  13. package/components/button/button.mjs +11 -0
  14. package/components/button/buttons.stories.ts +150 -0
  15. package/components/button/buttons.ts +282 -0
  16. package/components/button/examples.iso.js +18 -0
  17. package/components/button/fabSpec.js +65 -0
  18. package/components/button/test-button.mjs +16 -0
  19. package/components/card/_cards.scss +229 -0
  20. package/components/card/card.mjs +10 -0
  21. package/components/card/cards.ts +185 -0
  22. package/components/card/cardsSpec.js +269 -0
  23. package/components/carousel/_carousel.scss +93 -0
  24. package/components/carousel/_slider.scss +119 -0
  25. package/components/carousel/carousel.ts +693 -0
  26. package/components/carousel/carouselSpec.js +61 -0
  27. package/components/carousel/slider.ts +431 -0
  28. package/components/carousel/sliderSpec.js +99 -0
  29. package/components/checkbox/_checkboxes.scss +281 -0
  30. package/components/checkbox/checkbox.mdx +24 -0
  31. package/components/checkbox/checkbox.stories.ts +120 -0
  32. package/components/chip/chip.mjs +37 -0
  33. package/components/chip/chip.test.mjs +10 -0
  34. package/components/chip/chips.css +126 -0
  35. package/components/chip/chips.mdx +33 -0
  36. package/components/chip/chips.ts +440 -0
  37. package/components/chip/chipsSpec.js +165 -0
  38. package/components/chip/readme.md +323 -0
  39. package/components/collapsible/_collapsible.scss +82 -0
  40. package/components/collapsible/collapsible.ts +230 -0
  41. package/components/collapsible/collapsibleSpec.js +240 -0
  42. package/components/datepicker/_datepicker.scss +268 -0
  43. package/components/datepicker/datepicker.ts +1450 -0
  44. package/components/datepicker/datepickerSpec.js +162 -0
  45. package/components/dialog/_materialbox.scss +42 -0
  46. package/components/dialog/_modal.scss +79 -0
  47. package/components/dialog/materialbox.ts +446 -0
  48. package/components/dialog/materialboxSpec.js +37 -0
  49. package/components/dialog/modal.ts +111 -0
  50. package/components/dialog/modalSpec.js +83 -0
  51. package/components/dialog/readme.md +5 -0
  52. package/components/divider/readme.md +15 -0
  53. package/components/dropdown/_dropdown.scss +83 -0
  54. package/components/dropdown/dropdown.ts +653 -0
  55. package/components/dropdown/dropdownSpec.js +108 -0
  56. package/components/list/_collection.scss +114 -0
  57. package/components/list/_list.scss +6 -0
  58. package/components/list/list.mjs +58 -0
  59. package/components/list/list.test.mjs +16 -0
  60. package/components/loading/loading.mjs +26 -0
  61. package/components/loading/loading.test.mjs +10 -0
  62. package/components/navigation-bar/_old +582 -0
  63. package/components/navigation-bar/navigation-bar.mdx +20 -0
  64. package/components/navigation-bar/navigation-bar.stories.ts +39 -0
  65. package/components/navigation-drawer/_sidenav.scss +247 -0
  66. package/components/navigation-drawer/sidenav.ts +546 -0
  67. package/components/navigation-drawer/sidenavSpec.js +124 -0
  68. package/components/pagination/_pagination.scss +47 -0
  69. package/components/progress/_preloader.scss +416 -0
  70. package/components/radio-button/_radio-buttons.scss +112 -0
  71. package/components/radio-button/radio-buttons.mdx +28 -0
  72. package/components/radio-button/radio-buttons.stories.ts +51 -0
  73. package/components/search/autocomplete.ts +620 -0
  74. package/components/search/autocompleteSpec.js +321 -0
  75. package/components/slider/_range.scss +157 -0
  76. package/components/slider/range.ts +216 -0
  77. package/components/snackbar/_toast.scss +68 -0
  78. package/components/snackbar/toastSpec.js +132 -0
  79. package/components/snackbar/toasts.ts +312 -0
  80. package/components/switch/_switches.scss +121 -0
  81. package/components/tabs/_tabs.scss +171 -0
  82. package/components/tabs/tabs.mjs +31 -0
  83. package/components/tabs/tabs.ts +358 -0
  84. package/components/tabs/tabsSpec.js +180 -0
  85. package/components/textfield/_file-input.scss +41 -0
  86. package/components/textfield/_input-fields.scss +368 -0
  87. package/components/textfield/_select.scss +104 -0
  88. package/components/textfield/formfields.css +311 -0
  89. package/components/textfield/forms.ts +182 -0
  90. package/components/textfield/formsSpec.js +142 -0
  91. package/components/textfield/select.ts +472 -0
  92. package/components/textfield/selectSpec.js +390 -0
  93. package/components/textfield/textfield.mjs +29 -0
  94. package/components/textfield/textfield.test.mjs +10 -0
  95. package/components/timepicker/_timepicker.scss +362 -0
  96. package/components/timepicker/timepicker.ts +905 -0
  97. package/components/tooltip/_tooltip.scss +40 -0
  98. package/components/tooltip/tooltip.ts +380 -0
  99. package/components/tooltip/tooltipSpec.js +145 -0
  100. package/dist/css/materialize.colors.min.css +6 -0
  101. package/dist/css/materialize.css +7578 -6326
  102. package/dist/css/materialize.min.css +6 -13
  103. package/dist/css/materialize.min.css.map +1 -0
  104. package/dist/js/materialize.cjs.js +8410 -0
  105. package/dist/js/materialize.d.ts +2625 -0
  106. package/dist/js/materialize.js +8415 -12374
  107. package/dist/js/materialize.min.js +6 -6
  108. package/dist/js/materialize.mjs +8382 -0
  109. package/package.json +82 -47
  110. package/sass/_colors.scss +417 -0
  111. package/sass/_global.scss +490 -0
  112. package/sass/_grid.scss +172 -0
  113. package/sass/_table_of_contents.scss +28 -0
  114. package/sass/_tapTarget.scss +102 -0
  115. package/sass/_typography.scss +58 -0
  116. package/sass/_variables.scss +56 -0
  117. package/sass/materialize.scss +51 -0
  118. package/sass/mixins.module.scss +168 -0
  119. package/extras/noUiSlider/nouislider.css +0 -406
  120. package/extras/noUiSlider/nouislider.js +0 -2147
  121. package/extras/noUiSlider/nouislider.min.js +0 -1
  122. package/js/anime.min.js +0 -34
  123. package/js/autocomplete.js +0 -448
  124. package/js/buttons.js +0 -354
  125. package/js/cards.js +0 -40
  126. package/js/carousel.js +0 -717
  127. package/js/cash.js +0 -960
  128. package/js/characterCounter.js +0 -136
  129. package/js/chips.js +0 -481
  130. package/js/collapsible.js +0 -275
  131. package/js/component.js +0 -44
  132. package/js/datepicker.js +0 -975
  133. package/js/dropdown.js +0 -635
  134. package/js/forms.js +0 -275
  135. package/js/global.js +0 -444
  136. package/js/materialbox.js +0 -453
  137. package/js/modal.js +0 -382
  138. package/js/parallax.js +0 -138
  139. package/js/pushpin.js +0 -148
  140. package/js/range.js +0 -263
  141. package/js/scrollspy.js +0 -295
  142. package/js/select.js +0 -450
  143. package/js/sidenav.js +0 -580
  144. package/js/slider.js +0 -359
  145. package/js/tabs.js +0 -402
  146. package/js/tapTarget.js +0 -314
  147. package/js/timepicker.js +0 -647
  148. package/js/toasts.js +0 -310
  149. package/js/tooltip.js +0 -303
  150. package/js/waves.js +0 -335
@@ -0,0 +1,620 @@
1
+ import { Utils } from '../../src/utils';
2
+ import { Dropdown, DropdownOptions } from '../dropdown/dropdown';
3
+ import { Component, BaseOptions, InitElements, MElement } from '../../src/component';
4
+
5
+ export interface AutocompleteData {
6
+ /**
7
+ * A primitive value that can be converted to string.
8
+ * If "text" is not provided, it will also be used as "option text" as well
9
+ */
10
+ id: string | number;
11
+ /**
12
+ * This optional attribute is used as "display value" for the current entry.
13
+ * When provided, it will also be taken into consideration by the standard search function.
14
+ */
15
+ text?: string;
16
+ /**
17
+ * This optional attribute is used to provide a valid image URL to the current option.
18
+ */
19
+ image?: string;
20
+ /**
21
+ * Optional attributes which describes the option.
22
+ */
23
+ description?: string;
24
+ }
25
+
26
+ export interface AutocompleteOptions extends BaseOptions {
27
+ /**
28
+ * Data object defining autocomplete options with
29
+ * optional icon strings.
30
+ */
31
+ data: AutocompleteData[];
32
+ /**
33
+ * Flag which can be set if multiple values can be selected. The Result will be an Array.
34
+ * @default false
35
+ */
36
+ isMultiSelect: boolean;
37
+ /**
38
+ * Callback for when autocompleted.
39
+ */
40
+ onAutocomplete: (entries: AutocompleteData[]) => void;
41
+ /**
42
+ * Minimum number of characters before autocomplete starts.
43
+ * @default 1
44
+ */
45
+ minLength: number;
46
+ /**
47
+ * The height of the Menu which can be set via css-property.
48
+ * @default '300px'
49
+ */
50
+ maxDropDownHeight: string;
51
+ /**
52
+ * Function is called when the input text is altered and data can also be loaded asynchronously.
53
+ * If the results are collected the items in the list can be updated via the function setMenuItems(collectedItems).
54
+ * @param text Searched text.
55
+ * @param autocomplete Current autocomplete instance.
56
+ */
57
+ onSearch: (text: string, autocomplete: Autocomplete) => void;
58
+ /**
59
+ * If true will render the key from each item directly as HTML.
60
+ * User input MUST be properly sanitized first.
61
+ * @default false
62
+ */
63
+ allowUnsafeHTML: boolean;
64
+ /**
65
+ * Pass options object to select dropdown initialization.
66
+ * @default {}
67
+ */
68
+ dropdownOptions: Partial<DropdownOptions>;
69
+ /**
70
+ * Predefined selected values
71
+ */
72
+ selected: number[] | string[];
73
+ }
74
+
75
+ const _defaults: AutocompleteOptions = {
76
+ data: [], // Autocomplete data set
77
+ onAutocomplete: null, // Callback for when autocompleted
78
+ dropdownOptions: {
79
+ // Default dropdown options
80
+ autoFocus: false,
81
+ closeOnClick: false,
82
+ coverTrigger: false
83
+ },
84
+ minLength: 1, // Min characters before autocomplete starts
85
+ isMultiSelect: false,
86
+ onSearch: (text: string, autocomplete: Autocomplete) => {
87
+ const normSearch = text.toLocaleLowerCase();
88
+ autocomplete.setMenuItems(
89
+ autocomplete.data.filter(
90
+ (option) =>
91
+ option.id.toString().toLocaleLowerCase().includes(normSearch) ||
92
+ option.text?.toLocaleLowerCase().includes(normSearch)
93
+ )
94
+ );
95
+ },
96
+ maxDropDownHeight: '300px',
97
+ allowUnsafeHTML: false,
98
+ selected: [],
99
+ };
100
+
101
+ export class Autocomplete extends Component<AutocompleteOptions> {
102
+ declare el: HTMLInputElement;
103
+ /** If the autocomplete is open. */
104
+ isOpen: boolean;
105
+ /** Number of matching autocomplete options. */
106
+ count: number;
107
+ /** Index of the current selected option. */
108
+ activeIndex: number;
109
+ private oldVal: string;
110
+ private $active: HTMLElement | null;
111
+ private _mousedown: boolean;
112
+ container: HTMLElement;
113
+ /** Instance of the dropdown plugin for this autocomplete. */
114
+ dropdown: Dropdown;
115
+ static _keydown: boolean;
116
+ selectedValues: AutocompleteData[];
117
+ menuItems: AutocompleteData[];
118
+ data: AutocompleteData[];
119
+
120
+ constructor(el: HTMLInputElement, options: Partial<AutocompleteOptions>) {
121
+ super(el, options, Autocomplete);
122
+ this.el['M_Autocomplete'] = this;
123
+
124
+ this.options = {
125
+ ...Autocomplete.defaults,
126
+ ...options
127
+ };
128
+
129
+ this.isOpen = false;
130
+ this.count = 0;
131
+ this.activeIndex = -1;
132
+ this.oldVal = '';
133
+ this.selectedValues =
134
+ this.selectedValues ||
135
+ this.options.selected.map((value: number | string) => <AutocompleteData>{ id: value }) ||
136
+ [];
137
+ this.menuItems = this.options.data || [];
138
+ this.data = this.options.data || [];
139
+ this.$active = null;
140
+ this._mousedown = false;
141
+ this._setupDropdown();
142
+ this._setupEventHandlers();
143
+ }
144
+
145
+ static get defaults(): AutocompleteOptions {
146
+ return _defaults;
147
+ }
148
+
149
+ /**
150
+ * Initializes instance of Autocomplete.
151
+ * @param el HTML element.
152
+ * @param options Component options.
153
+ */
154
+ static init(el: HTMLInputElement, options?: Partial<AutocompleteOptions>): Autocomplete;
155
+ /**
156
+ * Initializes instances of Autocomplete.
157
+ * @param els HTML elements.
158
+ * @param options Component options.
159
+ */
160
+ static init(
161
+ els: InitElements<HTMLInputElement | MElement>,
162
+ options?: Partial<AutocompleteOptions>
163
+ ): Autocomplete[];
164
+ /**
165
+ * Initializes instances of Autocomplete.
166
+ * @param els HTML elements.
167
+ * @param options Component options.
168
+ */
169
+ static init(
170
+ els: HTMLInputElement | InitElements<HTMLInputElement | MElement>,
171
+ options: Partial<AutocompleteOptions> = {}
172
+ ): Autocomplete | Autocomplete[] {
173
+ return super.init(els, options, Autocomplete);
174
+ }
175
+
176
+ static getInstance(el: HTMLElement): Autocomplete {
177
+ return el['M_Autocomplete'];
178
+ }
179
+
180
+ destroy() {
181
+ this._removeEventHandlers();
182
+ this._removeDropdown();
183
+ this.el['M_Autocomplete'] = undefined;
184
+ }
185
+
186
+ _setupEventHandlers() {
187
+ this.el.addEventListener('blur', this._handleInputBlur);
188
+ this.el.addEventListener('keyup', this._handleInputKeyup);
189
+ this.el.addEventListener('focus', this._handleInputFocus);
190
+ this.el.addEventListener('keydown', this._handleInputKeydown);
191
+ this.el.addEventListener('click', this._handleInputClick);
192
+ this.container.addEventListener('mousedown', this._handleContainerMousedownAndTouchstart);
193
+ this.container.addEventListener('mouseup', this._handleContainerMouseupAndTouchend);
194
+ if (typeof window.ontouchstart !== 'undefined') {
195
+ this.container.addEventListener('touchstart', this._handleContainerMousedownAndTouchstart);
196
+ this.container.addEventListener('touchend', this._handleContainerMouseupAndTouchend);
197
+ }
198
+ }
199
+
200
+ _removeEventHandlers() {
201
+ this.el.removeEventListener('blur', this._handleInputBlur);
202
+ this.el.removeEventListener('keyup', this._handleInputKeyup);
203
+ this.el.removeEventListener('focus', this._handleInputFocus);
204
+ this.el.removeEventListener('keydown', this._handleInputKeydown);
205
+ this.el.removeEventListener('click', this._handleInputClick);
206
+ this.container.removeEventListener('mousedown', this._handleContainerMousedownAndTouchstart);
207
+ this.container.removeEventListener('mouseup', this._handleContainerMouseupAndTouchend);
208
+
209
+ if (typeof window.ontouchstart !== 'undefined') {
210
+ this.container.removeEventListener('touchstart', this._handleContainerMousedownAndTouchstart);
211
+ this.container.removeEventListener('touchend', this._handleContainerMouseupAndTouchend);
212
+ }
213
+ }
214
+
215
+ _setupDropdown() {
216
+ this.container = document.createElement('ul');
217
+ this.container.style.maxHeight = this.options.maxDropDownHeight;
218
+ this.container.id = `autocomplete-options-${Utils.guid()}`;
219
+ this.container.classList.add('autocomplete-content', 'dropdown-content');
220
+ this.container.ariaExpanded = 'true';
221
+ this.el.setAttribute('data-target', this.container.id);
222
+
223
+ this.menuItems.forEach((menuItem) => {
224
+ const itemElement = this._createDropdownItem(menuItem);
225
+ this.container.append(itemElement);
226
+ });
227
+
228
+ // ! Issue in Component Dropdown: _placeDropdown moves dom-position
229
+ this.el.parentElement.appendChild(this.container);
230
+
231
+ // Initialize dropdown
232
+ const dropdownOptions = {
233
+ ...Autocomplete.defaults.dropdownOptions,
234
+ ...this.options.dropdownOptions
235
+ };
236
+ // @todo shouldn't we conditionally check if dropdownOptions.onItemClick is set in first place?
237
+ const userOnItemClick = dropdownOptions.onItemClick;
238
+ // Ensuring the select Option call when user passes custom onItemClick function to dropdown
239
+ dropdownOptions.onItemClick = (li) => {
240
+ if (!li) return;
241
+ const entryID = li.getAttribute('data-id');
242
+ this.selectOption(entryID);
243
+ // Handle user declared onItemClick if needed
244
+ if (userOnItemClick && typeof userOnItemClick === 'function')
245
+ userOnItemClick.call(this.dropdown, this.el);
246
+ };
247
+ this.dropdown = Dropdown.init(this.el, dropdownOptions);
248
+
249
+ // ! Workaround for Label: move label up again
250
+ // TODO: Just use PopperJS in future!
251
+ const label = this.el.parentElement.querySelector('label');
252
+ if (label) this.el.after(label);
253
+
254
+ // Sketchy removal of dropdown click handler
255
+ this.el.removeEventListener('click', this.dropdown._handleClick);
256
+ if (!this.options.isMultiSelect && !(this.options.selected.length === 0)) {
257
+ const selectedValue = this.menuItems.filter(
258
+ (value) => value.id === this.selectedValues[0].id
259
+ );
260
+ this.el.value = selectedValue[0].text;
261
+ }
262
+ // Set Value if already set in HTML
263
+ if (this.el.value) this.selectOption(this.el.value);
264
+ // Add StatusInfo
265
+ const div = document.createElement('div');
266
+ div.classList.add('status-info');
267
+ div.setAttribute('style', 'position: absolute;right:0;top:0;');
268
+ this.el.parentElement.appendChild(div);
269
+ this._updateSelectedInfo();
270
+ }
271
+
272
+ _removeDropdown() {
273
+ this.container.ariaExpanded = 'false';
274
+ this.container.parentNode.removeChild(this.container);
275
+ }
276
+
277
+ _handleInputBlur = () => {
278
+ if (!this._mousedown) {
279
+ this.close();
280
+ this._resetAutocomplete();
281
+ }
282
+ };
283
+
284
+ _handleInputKeyup = (e: KeyboardEvent) => {
285
+ if (e.type === 'keyup') Autocomplete._keydown = false;
286
+ this.count = 0;
287
+ const actualValue = this.el.value.toLocaleLowerCase();
288
+ // Don't capture enter or arrow key usage.
289
+ if (
290
+ Utils.keys.ENTER.includes(e.key) ||
291
+ Utils.keys.ARROW_UP.includes(e.key) ||
292
+ Utils.keys.ARROW_DOWN.includes(e.key)
293
+ )
294
+ return;
295
+ // Check if the input isn't empty
296
+ // Check if focus triggered by tab
297
+ if (this.oldVal !== actualValue && Utils.tabPressed) {
298
+ this.open();
299
+ }
300
+ this._inputChangeDetection(actualValue);
301
+ };
302
+
303
+ _handleInputFocus = () => {
304
+ this.count = 0;
305
+ const actualValue = this.el.value.toLocaleLowerCase();
306
+ this._inputChangeDetection(actualValue);
307
+ };
308
+
309
+ _inputChangeDetection = (value: string) => {
310
+ // Value has changed!
311
+ if (this.oldVal !== value) {
312
+ this._setStatusLoading();
313
+ this.options.onSearch(this.el.value, this);
314
+ }
315
+ // Reset Single-Select when Input cleared
316
+ if (!this.options.isMultiSelect && this.el.value.length === 0) {
317
+ this.selectedValues = [];
318
+ this._triggerChanged();
319
+ }
320
+ this.oldVal = value;
321
+ };
322
+
323
+ _handleInputKeydown = (e: KeyboardEvent) => {
324
+ Autocomplete._keydown = true;
325
+ // Arrow keys and enter key usage
326
+ const numItems = this.container.querySelectorAll('li').length;
327
+ // select element on Enter
328
+ if (Utils.keys.ENTER.includes(e.key) && this.activeIndex >= 0) {
329
+ const liElement = this.container.querySelectorAll('li')[this.activeIndex];
330
+ if (liElement) {
331
+ this.selectOption(liElement.getAttribute('data-id'));
332
+ e.preventDefault();
333
+ }
334
+ return;
335
+ }
336
+ // Capture up and down key
337
+ if (Utils.keys.ARROW_UP.includes(e.key) || Utils.keys.ARROW_DOWN.includes(e.key)) {
338
+ e.preventDefault();
339
+ if (Utils.keys.ARROW_UP.includes(e.key) && this.activeIndex > 0) this.activeIndex--;
340
+ if (Utils.keys.ARROW_DOWN.includes(e.key) && this.activeIndex < numItems - 1)
341
+ this.activeIndex++;
342
+ this.$active?.classList.remove('active');
343
+ if (this.activeIndex >= 0) {
344
+ this.$active = this.container.querySelectorAll('li')[this.activeIndex];
345
+ this.$active?.classList.add('active');
346
+ // Focus selected
347
+ this.container.children[this.activeIndex].scrollIntoView({
348
+ behavior: 'smooth',
349
+ block: 'nearest',
350
+ inline: 'nearest'
351
+ });
352
+ }
353
+ }
354
+ };
355
+
356
+ _handleInputClick = () => {
357
+ this.open();
358
+ };
359
+
360
+ _handleContainerMousedownAndTouchstart = () => {
361
+ this._mousedown = true;
362
+ };
363
+
364
+ _handleContainerMouseupAndTouchend = () => {
365
+ this._mousedown = false;
366
+ };
367
+
368
+ _resetCurrentElementPosition() {
369
+ this.activeIndex = -1;
370
+ this.$active?.classList.remove('active');
371
+ }
372
+
373
+ _resetAutocomplete() {
374
+ this.container.replaceChildren();
375
+ this._resetCurrentElementPosition();
376
+ this.oldVal = null;
377
+ this.isOpen = false;
378
+ this._mousedown = false;
379
+ }
380
+
381
+ _highlightPartialText(input: string, label: string) {
382
+ const start = label.toLocaleLowerCase().indexOf('' + input.toLocaleLowerCase() + '');
383
+ const end = start + input.length - 1;
384
+ //custom filters may return results where the string does not match any part
385
+ if (start == -1 || end == -1) {
386
+ return [label, '', ''];
387
+ }
388
+ return [label.slice(0, start), label.slice(start, end + 1), label.slice(end + 1)];
389
+ }
390
+
391
+ _createDropdownItem(entry: AutocompleteData) {
392
+ const item = document.createElement('li');
393
+ item.setAttribute('data-id', <string>entry.id);
394
+ item.setAttribute(
395
+ 'style',
396
+ 'display:grid; grid-auto-flow: column; user-select: none; align-items: center;'
397
+ );
398
+ item.tabIndex = 0;
399
+ // Checkbox
400
+ if (this.options.isMultiSelect) {
401
+ item.innerHTML = `
402
+ <div class="item-selection" style="text-align:center;">
403
+ <input type="checkbox"${
404
+ this.selectedValues.some((sel) => sel.id === entry.id) ? ' checked="checked"' : ''
405
+ }><span style="padding-left:21px;"></span>
406
+ </div>`;
407
+ }
408
+ // Image
409
+ if (entry.image) {
410
+ const img = document.createElement('img');
411
+ img.classList.add('circle');
412
+ img.src = entry.image;
413
+ item.appendChild(img);
414
+ }
415
+
416
+ // Text
417
+ const inputText = this.el.value.toLocaleLowerCase();
418
+ const parts = this._highlightPartialText(inputText, (entry.text || entry.id).toString());
419
+ const div = document.createElement('div');
420
+ div.setAttribute('style', 'line-height:1.2;font-weight:500;');
421
+ if (this.options.allowUnsafeHTML) {
422
+ div.innerHTML = parts[0] + '<span class="highlight">' + parts[1] + '</span>' + parts[2];
423
+ } else {
424
+ div.appendChild(document.createTextNode(parts[0]));
425
+ if (parts[1]) {
426
+ const highlight = document.createElement('span');
427
+ highlight.textContent = parts[1];
428
+ highlight.classList.add('highlight');
429
+ div.appendChild(highlight);
430
+ div.appendChild(document.createTextNode(parts[2]));
431
+ }
432
+ }
433
+
434
+ const itemText = document.createElement('div');
435
+ itemText.classList.add('item-text');
436
+ itemText.setAttribute('style', 'padding:5px;overflow:hidden;');
437
+ item.appendChild(itemText);
438
+ item.querySelector('.item-text').appendChild(div);
439
+ // Description
440
+ if (
441
+ typeof entry.description === 'string' ||
442
+ (typeof entry.description === 'number' && !isNaN(entry.description))
443
+ ) {
444
+ const description = document.createElement('small');
445
+ description.setAttribute(
446
+ 'style',
447
+ 'line-height:1.3;color:grey;white-space:nowrap;text-overflow:ellipsis;display:block;width:90%;overflow:hidden;'
448
+ );
449
+ description.innerText = entry.description;
450
+ item.querySelector('.item-text').appendChild(description);
451
+ }
452
+ // Set Grid
453
+ const getGridConfig = () => {
454
+ if (this.options.isMultiSelect) {
455
+ if (entry.image) return '40px min-content auto'; // cb-img-txt
456
+ return '40px auto'; // cb-txt
457
+ }
458
+ if (entry.image) return 'min-content auto'; // img-txt
459
+ return 'auto'; // txt
460
+ };
461
+ item.style.gridTemplateColumns = getGridConfig();
462
+ return item;
463
+ }
464
+
465
+ _renderDropdown() {
466
+ this._resetAutocomplete();
467
+ // Check if Data is empty
468
+ if (this.menuItems.length === 0) {
469
+ this.menuItems = this.selectedValues; // Show selected Items
470
+ }
471
+ for (let i = 0; i < this.menuItems.length; i++) {
472
+ const item = this._createDropdownItem(this.menuItems[i]);
473
+ this.container.append(item);
474
+ }
475
+ }
476
+
477
+ _setStatusLoading() {
478
+ this.el.parentElement.querySelector(
479
+ '.status-info'
480
+ ).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">
481
+ <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>
482
+ <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>
483
+ <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>
484
+ </svg></div>`;
485
+ }
486
+
487
+ _updateSelectedInfo() {
488
+ const statusElement = this.el.parentElement.querySelector('.status-info');
489
+ if (statusElement) {
490
+ if (this.options.isMultiSelect)
491
+ statusElement.innerHTML = this.selectedValues.length.toString();
492
+ else statusElement.innerHTML = '';
493
+ }
494
+ }
495
+
496
+ _refreshInputText() {
497
+ if (this.selectedValues.length === 1) {
498
+ const entry = this.selectedValues[0];
499
+ this.el.value = entry.text || <string>entry.id; // Write Text to Input
500
+ }
501
+ }
502
+
503
+ _triggerChanged() {
504
+ this.el.dispatchEvent(new Event('change'));
505
+ // Trigger Autocomplete Event
506
+ if (typeof this.options.onAutocomplete === 'function')
507
+ this.options.onAutocomplete.call(this, this.selectedValues);
508
+ }
509
+
510
+ /**
511
+ * Show autocomplete.
512
+ */
513
+ open = () => {
514
+ const inputText = this.el.value.toLocaleLowerCase();
515
+ this._resetAutocomplete();
516
+ if (inputText.length >= this.options.minLength) {
517
+ this.isOpen = true;
518
+ this._renderDropdown();
519
+ }
520
+ // Open dropdown
521
+ if (!this.dropdown.isOpen) {
522
+ setTimeout(() => {
523
+ this.dropdown.open();
524
+ }, 0); // TODO: why?
525
+ } else this.dropdown.recalculateDimensions(); // Recalculate dropdown when its already open
526
+ };
527
+
528
+ /**
529
+ * Hide autocomplete.
530
+ */
531
+ close = () => {
532
+ this.dropdown.close();
533
+ };
534
+
535
+ /**
536
+ * Updates the visible or selectable items shown in the menu.
537
+ * @param menuItems Items to be available.
538
+ * @param selected Selected item ids
539
+ * @param open Option to conditionally open dropdown
540
+ * @param initial Condition to set initial data
541
+ */
542
+ setMenuItems(
543
+ menuItems: AutocompleteData[],
544
+ selected: number[] | string[] = null,
545
+ open: boolean = true,
546
+ initial: boolean = false,
547
+ ) {
548
+ this.menuItems = menuItems;
549
+ this.options.data = menuItems;
550
+ if (initial) {
551
+ this.data = menuItems;
552
+ }
553
+ if (selected) {
554
+ this.selectedValues = this.menuItems.filter(
555
+ (item) => !(selected.indexOf(<never>item.id) === -1)
556
+ );
557
+ }
558
+ if (this.options.isMultiSelect) {
559
+ this._renderDropdown();
560
+ }
561
+ if (open) this.open();
562
+ this._updateSelectedInfo();
563
+ this._triggerChanged();
564
+ }
565
+
566
+ /**
567
+ * Sets selected values.
568
+ * @deprecated @see https://github.com/materializecss/materialize/issues/552
569
+ * @param entries
570
+ */
571
+ setValues(entries: AutocompleteData[]) {
572
+ this.selectedValues = entries;
573
+ this._updateSelectedInfo();
574
+ if (!this.options.isMultiSelect) {
575
+ this._refreshInputText();
576
+ }
577
+ this._triggerChanged();
578
+ }
579
+
580
+ /**
581
+ * Select a specific autocomplete option via id-property.
582
+ * @param id The id of a data-entry.
583
+ */
584
+ selectOption(id: number | string) {
585
+ const entry = this.menuItems.find((item) => item.id == id);
586
+ if (!entry) return;
587
+ // Toggle Checkbox
588
+ /* const li = this.container.querySelector('li[data-id="' + id + '"]');
589
+ if (!li) return;*/
590
+ if (this.options.isMultiSelect) {
591
+ /* const checkbox = <HTMLInputElement | null>li.querySelector('input[type="checkbox"]');
592
+ checkbox.checked = !checkbox.checked;*/
593
+ if (
594
+ !(this.selectedValues.filter((selectedEntry) => selectedEntry.id === entry.id).length >= 1)
595
+ )
596
+ this.selectedValues.push(entry);
597
+ else
598
+ this.selectedValues = this.selectedValues.filter(
599
+ (selectedEntry) => selectedEntry.id !== entry.id
600
+ );
601
+ this._renderDropdown();
602
+ this.el.focus();
603
+ } else {
604
+ // Single-Select
605
+ this.selectedValues = [entry];
606
+ this._refreshInputText();
607
+ this._resetAutocomplete();
608
+ this.close();
609
+ }
610
+ this._updateSelectedInfo();
611
+ this._triggerChanged();
612
+ }
613
+
614
+ selectOptions(ids: []) {
615
+ const entries = this.menuItems.filter((item) => !(ids.indexOf(<never>item.id) === -1));
616
+ if (!entries) return;
617
+ this.selectedValues = entries;
618
+ this._renderDropdown();
619
+ }
620
+ }