slim-select 2.0.2 → 2.1.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 (45) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/render.d.ts +1 -1
  3. package/dist/slimselect.cjs.js +54 -16
  4. package/dist/slimselect.es.js +54 -16
  5. package/dist/slimselect.global.js +54 -16
  6. package/dist/slimselect.js +54 -16
  7. package/dist/slimselect.min.js +1 -1
  8. package/dist/slimselect.umd.js +54 -16
  9. package/dist/slimselect.umd.min.js +1 -1
  10. package/docs/assets/index.css +1 -1
  11. package/docs/assets/index.js +7 -7
  12. package/docs/assets/index3.js +25 -16
  13. package/docs/assets/index5.js +145 -9
  14. package/docs/assets/install.js +4 -4
  15. package/package.json +11 -3
  16. package/src/docs/app.vue +4 -4
  17. package/src/docs/{images → assets/images}/check-mark.svg +0 -0
  18. package/src/docs/{images → assets/images}/chrome.svg +0 -0
  19. package/src/docs/{images → assets/images}/firefox.svg +0 -0
  20. package/src/docs/{images → assets/images}/github.png +0 -0
  21. package/src/docs/{images → assets/images}/ie.svg +0 -0
  22. package/src/docs/{images → assets/images}/npm.png +0 -0
  23. package/src/docs/{images → assets/images}/opera.svg +0 -0
  24. package/src/docs/{images → assets/images}/safari.svg +0 -0
  25. package/src/docs/assets/scss/misc.scss +7 -0
  26. package/src/docs/env.d.ts +1 -0
  27. package/src/docs/pages/events/addable.vue +22 -6
  28. package/src/docs/pages/events/before_after_change.vue +15 -1
  29. package/src/docs/pages/events/search.vue +2 -1
  30. package/src/docs/pages/frameworks/vue.vue +373 -7
  31. package/src/docs/pages/home.vue +18 -18
  32. package/src/docs/pages/methods/get_data.vue +2 -1
  33. package/src/docs/pages/methods/open_close.vue +1 -1
  34. package/src/slim-select/index.ts +1 -1
  35. package/src/slim-select/render.ts +83 -25
  36. package/src/slim-select/rollup.config.mjs +5 -0
  37. package/src/slim-select/select.ts +14 -5
  38. package/src/vue/dist/slimselectvue.es.js +1655 -0
  39. package/src/vue/dist/slimselectvue.global.js +1658 -0
  40. package/src/vue/dist/slimselectvue.ssr.js +1657 -0
  41. package/src/vue/hello.ts +5 -0
  42. package/src/vue/package.json +27 -0
  43. package/src/vue/rollup.config.mjs +44 -0
  44. package/src/vue/slimselect.vue +39 -5
  45. package/src/vue/tsconfig.json +14 -0
@@ -0,0 +1,1657 @@
1
+ 'use strict';
2
+
3
+ var vue = require('vue');
4
+
5
+ function generateID() {
6
+ return Math.random().toString(36).substring(2, 10);
7
+ }
8
+ function hasClassInTree(element, className) {
9
+ function hasClass(e, c) {
10
+ if (c && e && e.classList && e.classList.contains(c)) {
11
+ return e;
12
+ }
13
+ if (c && e && e.dataset && e.dataset.id && e.dataset.id === className) {
14
+ return e;
15
+ }
16
+ return null;
17
+ }
18
+ function parentByClass(e, c) {
19
+ if (!e || e === document) {
20
+ return null;
21
+ }
22
+ else if (hasClass(e, c)) {
23
+ return e;
24
+ }
25
+ else {
26
+ return parentByClass(e.parentNode, c);
27
+ }
28
+ }
29
+ return hasClass(element, className) || parentByClass(element, className);
30
+ }
31
+ function debounce(func, wait = 50, immediate = false) {
32
+ let timeout;
33
+ return function (...args) {
34
+ const context = self;
35
+ const later = () => {
36
+ timeout = null;
37
+ if (!immediate) {
38
+ func.apply(context, args);
39
+ }
40
+ };
41
+ const callNow = immediate && !timeout;
42
+ clearTimeout(timeout);
43
+ timeout = setTimeout(later, wait);
44
+ if (callNow) {
45
+ func.apply(context, args);
46
+ }
47
+ };
48
+ }
49
+ function kebabCase(str) {
50
+ const result = str.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g, (match) => '-' + match.toLowerCase());
51
+ return str[0] === str[0].toUpperCase() ? result.substring(1) : result;
52
+ }
53
+
54
+ class Optgroup {
55
+ constructor(optgroup) {
56
+ this.id = !optgroup.id || optgroup.id === '' ? generateID() : optgroup.id;
57
+ this.label = optgroup.label || '';
58
+ this.options = [];
59
+ if (optgroup.options) {
60
+ for (const o of optgroup.options) {
61
+ this.options.push(new Option(o));
62
+ }
63
+ }
64
+ }
65
+ }
66
+ class Option {
67
+ constructor(option) {
68
+ this.id = !option.id || option.id === '' ? generateID() : option.id;
69
+ this.value = option.value === undefined ? option.text : option.value;
70
+ this.text = option.text || '';
71
+ this.html = option.html || '';
72
+ this.selected = option.selected !== undefined ? option.selected : false;
73
+ this.display = option.display !== undefined ? option.display : true;
74
+ this.disabled = option.disabled !== undefined ? option.disabled : false;
75
+ this.mandatory = option.mandatory !== undefined ? option.mandatory : false;
76
+ this.placeholder = option.placeholder !== undefined ? option.placeholder : false;
77
+ this.class = option.class || '';
78
+ this.style = option.style || '';
79
+ this.data = option.data || {};
80
+ }
81
+ }
82
+ class Store {
83
+ constructor(type, data) {
84
+ this.selectType = 'single';
85
+ this.data = [];
86
+ this.selectType = type;
87
+ this.setData(data);
88
+ }
89
+ validateDataArray(data) {
90
+ if (!Array.isArray(data)) {
91
+ return new Error('Data must be an array');
92
+ }
93
+ for (let dataObj of data) {
94
+ if (dataObj instanceof Optgroup || 'label' in dataObj) {
95
+ if (!('label' in dataObj)) {
96
+ return new Error('Optgroup must have a label');
97
+ }
98
+ if ('options' in dataObj && dataObj.options) {
99
+ for (let option of dataObj.options) {
100
+ return this.validateOption(option);
101
+ }
102
+ }
103
+ }
104
+ else if (dataObj instanceof Option || 'text' in dataObj) {
105
+ return this.validateOption(dataObj);
106
+ }
107
+ else {
108
+ return new Error('Data object must be a valid optgroup or option');
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+ validateOption(option) {
114
+ if (!('text' in option)) {
115
+ return new Error('Option must have a text');
116
+ }
117
+ return null;
118
+ }
119
+ partialToFullData(data) {
120
+ let dataFinal = [];
121
+ data.forEach((dataObj) => {
122
+ if (dataObj instanceof Optgroup || 'label' in dataObj) {
123
+ let optOptions = [];
124
+ if ('options' in dataObj && dataObj.options) {
125
+ dataObj.options.forEach((option) => {
126
+ optOptions.push(new Option(option));
127
+ });
128
+ }
129
+ if (optOptions.length > 0) {
130
+ dataFinal.push(new Optgroup(dataObj));
131
+ }
132
+ }
133
+ if (dataObj instanceof Option || 'text' in dataObj) {
134
+ dataFinal.push(new Option(dataObj));
135
+ }
136
+ });
137
+ return dataFinal;
138
+ }
139
+ setData(data) {
140
+ this.data = this.partialToFullData(data);
141
+ if (this.selectType === 'single') {
142
+ this.setSelectedBy('value', this.getSelected());
143
+ }
144
+ }
145
+ getData() {
146
+ return this.filter(null, true);
147
+ }
148
+ getDataOptions() {
149
+ return this.filter(null, false);
150
+ }
151
+ addOption(option) {
152
+ this.setData(this.getData().concat(new Option(option)));
153
+ }
154
+ setSelectedBy(selectedType, selectedValues) {
155
+ let firstOption = null;
156
+ let hasSelected = false;
157
+ for (let dataObj of this.data) {
158
+ if (dataObj instanceof Optgroup) {
159
+ for (let option of dataObj.options) {
160
+ if (!firstOption) {
161
+ firstOption = option;
162
+ }
163
+ option.selected = hasSelected ? false : selectedValues.includes(option[selectedType]);
164
+ if (option.selected && this.selectType === 'single') {
165
+ hasSelected = true;
166
+ }
167
+ }
168
+ }
169
+ if (dataObj instanceof Option) {
170
+ if (!firstOption) {
171
+ firstOption = dataObj;
172
+ }
173
+ dataObj.selected = hasSelected ? false : selectedValues.includes(dataObj[selectedType]);
174
+ if (dataObj.selected && this.selectType === 'single') {
175
+ hasSelected = true;
176
+ }
177
+ }
178
+ }
179
+ if (this.selectType === 'single' && firstOption && !hasSelected) {
180
+ firstOption.selected = true;
181
+ }
182
+ }
183
+ getSelected() {
184
+ let selectedOptions = this.getSelectedOptions();
185
+ let selectedValues = [];
186
+ selectedOptions.forEach((option) => {
187
+ selectedValues.push(option.value);
188
+ });
189
+ return selectedValues;
190
+ }
191
+ getSelectedOptions() {
192
+ return this.filter((opt) => {
193
+ return opt.selected;
194
+ }, false);
195
+ }
196
+ getSelectedIDs() {
197
+ let selectedOptions = this.getSelectedOptions();
198
+ let selectedIDs = [];
199
+ selectedOptions.forEach((op) => {
200
+ selectedIDs.push(op.id);
201
+ });
202
+ return selectedIDs;
203
+ }
204
+ getOptionByID(id) {
205
+ let options = this.filter((opt) => {
206
+ return opt.id === id;
207
+ }, false);
208
+ return options.length ? options[0] : null;
209
+ }
210
+ search(search, searchFilter) {
211
+ search = search.trim();
212
+ if (search === '') {
213
+ return this.getData();
214
+ }
215
+ return this.filter((opt) => {
216
+ return searchFilter(opt, search);
217
+ }, true);
218
+ }
219
+ filter(filter, includeOptgroup) {
220
+ const dataSearch = [];
221
+ this.data.forEach((dataObj) => {
222
+ if (dataObj instanceof Optgroup) {
223
+ if (!includeOptgroup) {
224
+ dataObj.options.forEach((option) => {
225
+ if (filter && filter(option)) {
226
+ dataSearch.push(option);
227
+ }
228
+ });
229
+ }
230
+ else {
231
+ let optOptions = [];
232
+ dataObj.options.forEach((option) => {
233
+ if (!filter || filter(option)) {
234
+ optOptions.push(new Option(option));
235
+ }
236
+ });
237
+ if (optOptions.length > 0) {
238
+ dataSearch.push(new Optgroup({ id: dataObj.id, label: dataObj.label, options: optOptions }));
239
+ }
240
+ }
241
+ }
242
+ if (dataObj instanceof Option) {
243
+ if (!filter || filter(dataObj)) {
244
+ dataSearch.push(new Option(dataObj));
245
+ }
246
+ }
247
+ });
248
+ return dataSearch;
249
+ }
250
+ }
251
+
252
+ class Render {
253
+ constructor(settings, store, callbacks) {
254
+ this.classes = {
255
+ main: 'ss-main',
256
+ placeholder: 'ss-placeholder',
257
+ values: 'ss-values',
258
+ single: 'ss-single',
259
+ value: 'ss-value',
260
+ valueText: 'ss-value-text',
261
+ valueDelete: 'ss-value-delete',
262
+ valueOut: 'ss-value-out',
263
+ deselect: 'ss-deselect',
264
+ deselectPath: 'M10,10 L90,90 M10,90 L90,10',
265
+ arrow: 'ss-arrow',
266
+ arrowClose: 'M10,30 L50,70 L90,30',
267
+ arrowOpen: 'M10,70 L50,30 L90,70',
268
+ content: 'ss-content',
269
+ openAbove: 'ss-open-above',
270
+ openBelow: 'ss-open-below',
271
+ search: 'ss-search',
272
+ searchHighlighter: 'ss-search-highlight',
273
+ searching: 'ss-searching',
274
+ addable: 'ss-addable',
275
+ addablePath: 'M50,10 L50,90 M10,50 L90,50',
276
+ list: 'ss-list',
277
+ optgroup: 'ss-optgroup',
278
+ optgroupLabel: 'ss-optgroup-label',
279
+ optgroupSelectable: 'ss-optgroup-selectable',
280
+ option: 'ss-option',
281
+ optionSelected: 'ss-option-selected',
282
+ optionDelete: 'M10,10 L90,90 M10,90 L90,10',
283
+ highlighted: 'ss-highlighted',
284
+ error: 'ss-error',
285
+ disabled: 'ss-disabled',
286
+ hide: 'ss-hide',
287
+ };
288
+ this.store = store;
289
+ this.settings = settings;
290
+ this.callbacks = callbacks;
291
+ this.main = this.mainDiv();
292
+ this.content = this.contentDiv();
293
+ this.renderValues();
294
+ this.renderOptions(this.store.getData());
295
+ this.settings.contentLocation.appendChild(this.content.main);
296
+ }
297
+ enable() {
298
+ this.main.main.classList.remove(this.classes.disabled);
299
+ this.content.search.input.disabled = false;
300
+ }
301
+ disable() {
302
+ this.main.main.classList.add(this.classes.disabled);
303
+ this.content.search.input.disabled = true;
304
+ }
305
+ open() {
306
+ this.main.arrow.path.setAttribute('d', this.classes.arrowOpen);
307
+ this.main.main.classList.add(this.settings.openPosition === 'up' ? this.classes.openAbove : this.classes.openBelow);
308
+ this.moveContent();
309
+ this.renderOptions(this.store.getData());
310
+ if (this.settings.contentPosition === 'relative') {
311
+ this.moveContentBelow();
312
+ }
313
+ else if (this.settings.openPosition.toLowerCase() === 'up') {
314
+ this.moveContentAbove();
315
+ }
316
+ else if (this.settings.openPosition.toLowerCase() === 'down') {
317
+ this.moveContentBelow();
318
+ }
319
+ else {
320
+ if (this.putContent(this.content.main, this.settings.isOpen) === 'up') {
321
+ this.moveContentAbove();
322
+ }
323
+ else {
324
+ this.moveContentBelow();
325
+ }
326
+ }
327
+ const selectedOptions = this.store.getSelectedOptions();
328
+ if (selectedOptions.length) {
329
+ const selectedId = selectedOptions[selectedOptions.length - 1].id;
330
+ const selectedOption = this.content.list.querySelector('[data-id="' + selectedId + '"]');
331
+ if (selectedOption) {
332
+ this.ensureElementInView(this.content.list, selectedOption);
333
+ }
334
+ }
335
+ }
336
+ close() {
337
+ this.main.main.classList.remove(this.classes.openAbove);
338
+ this.main.main.classList.remove(this.classes.openBelow);
339
+ this.content.main.classList.remove(this.classes.openAbove);
340
+ this.content.main.classList.remove(this.classes.openBelow);
341
+ this.main.arrow.path.setAttribute('d', this.classes.arrowClose);
342
+ }
343
+ mainDiv() {
344
+ const main = document.createElement('div');
345
+ main.tabIndex = 0;
346
+ main.style.cssText = this.settings.style !== '' ? this.settings.style : '';
347
+ main.className = '';
348
+ main.classList.add(this.settings.id);
349
+ main.classList.add(this.classes.main);
350
+ if (this.settings.class) {
351
+ for (const c of this.settings.class) {
352
+ if (c.trim() !== '') {
353
+ main.classList.add(c.trim());
354
+ }
355
+ }
356
+ }
357
+ main.onfocus = () => {
358
+ if (this.settings.triggerFocus) {
359
+ this.callbacks.open();
360
+ }
361
+ };
362
+ main.onkeydown = (e) => {
363
+ switch (e.key) {
364
+ case 'ArrowUp':
365
+ case 'ArrowDown':
366
+ this.callbacks.open();
367
+ e.key === 'ArrowDown' ? this.highlight('down') : this.highlight('up');
368
+ return false;
369
+ case 'Tab':
370
+ this.callbacks.close();
371
+ return true;
372
+ case 'Enter':
373
+ const highlighted = this.content.list.querySelector('.' + this.classes.highlighted);
374
+ if (highlighted) {
375
+ highlighted.click();
376
+ }
377
+ return false;
378
+ case 'Escape':
379
+ this.callbacks.close();
380
+ return false;
381
+ }
382
+ };
383
+ main.onclick = (e) => {
384
+ if (!this.settings.isEnabled) {
385
+ return;
386
+ }
387
+ this.settings.isOpen ? this.callbacks.close() : this.callbacks.open();
388
+ };
389
+ const values = document.createElement('div');
390
+ values.classList.add(this.classes.values);
391
+ main.appendChild(values);
392
+ const deselect = document.createElement('div');
393
+ deselect.classList.add(this.classes.deselect);
394
+ if (!this.settings.allowDeselect || this.settings.isMultiple) {
395
+ deselect.classList.add(this.classes.hide);
396
+ }
397
+ deselect.onclick = (e) => {
398
+ e.stopPropagation();
399
+ if (!this.settings.isEnabled) {
400
+ return;
401
+ }
402
+ let shouldDelete = true;
403
+ const before = this.store.getSelectedOptions();
404
+ const after = [];
405
+ if (this.callbacks.beforeChange) {
406
+ shouldDelete = this.callbacks.beforeChange(after, before) === true;
407
+ }
408
+ if (shouldDelete) {
409
+ this.callbacks.setSelected(['']);
410
+ if (this.settings.closeOnSelect) {
411
+ this.callbacks.close();
412
+ }
413
+ if (this.callbacks.afterChange) {
414
+ this.callbacks.afterChange(after);
415
+ }
416
+ }
417
+ };
418
+ const deselectSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
419
+ deselectSvg.setAttribute('viewBox', '0 0 100 100');
420
+ const deselectPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
421
+ deselectPath.setAttribute('d', this.classes.deselectPath);
422
+ deselectSvg.appendChild(deselectPath);
423
+ deselect.appendChild(deselectSvg);
424
+ main.appendChild(deselect);
425
+ const arrow = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
426
+ arrow.classList.add(this.classes.arrow);
427
+ arrow.setAttribute('viewBox', '0 0 100 100');
428
+ const arrowPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
429
+ arrowPath.setAttribute('d', this.classes.arrowClose);
430
+ if (this.settings.alwaysOpen) {
431
+ arrow.classList.add(this.classes.hide);
432
+ }
433
+ arrow.appendChild(arrowPath);
434
+ main.appendChild(arrow);
435
+ return {
436
+ main: main,
437
+ values: values,
438
+ deselect: {
439
+ main: deselect,
440
+ svg: deselectSvg,
441
+ path: deselectPath,
442
+ },
443
+ arrow: {
444
+ main: arrow,
445
+ path: arrowPath,
446
+ },
447
+ };
448
+ }
449
+ mainFocus(trigger) {
450
+ if (!trigger) {
451
+ this.settings.triggerFocus = false;
452
+ }
453
+ this.main.main.focus({ preventScroll: true });
454
+ this.settings.triggerFocus = true;
455
+ }
456
+ placeholder() {
457
+ const placeholderOption = this.store.filter((o) => o.placeholder, false);
458
+ let placeholderText = this.settings.placeholderText;
459
+ if (placeholderOption.length) {
460
+ if (placeholderOption[0].html !== '') {
461
+ placeholderText = placeholderOption[0].html;
462
+ }
463
+ else if (placeholderOption[0].text !== '') {
464
+ placeholderText = placeholderOption[0].text;
465
+ }
466
+ }
467
+ const placeholder = document.createElement('div');
468
+ placeholder.classList.add(this.classes.placeholder);
469
+ placeholder.innerHTML = placeholderText;
470
+ return placeholder;
471
+ }
472
+ renderValues() {
473
+ if (!this.settings.isMultiple) {
474
+ this.renderSingleValue();
475
+ return;
476
+ }
477
+ this.renderMultipleValues();
478
+ }
479
+ renderSingleValue() {
480
+ const selected = this.store.filter((o) => {
481
+ return o.selected && !o.placeholder;
482
+ }, false);
483
+ const selectedSingle = selected.length > 0 ? selected[0] : null;
484
+ if (!selectedSingle) {
485
+ this.main.values.innerHTML = this.placeholder().outerHTML;
486
+ }
487
+ else {
488
+ const singleValue = document.createElement('div');
489
+ singleValue.classList.add(this.classes.single);
490
+ singleValue.innerHTML = selectedSingle.html ? selectedSingle.html : selectedSingle.text;
491
+ this.main.values.innerHTML = singleValue.outerHTML;
492
+ }
493
+ if (!this.settings.allowDeselect || !selected.length) {
494
+ this.main.deselect.main.classList.add(this.classes.hide);
495
+ }
496
+ else {
497
+ this.main.deselect.main.classList.remove(this.classes.hide);
498
+ }
499
+ }
500
+ renderMultipleValues() {
501
+ let currentNodes = this.main.values.childNodes;
502
+ let selectedOptions = this.store.filter((opt) => {
503
+ return opt.selected && opt.display;
504
+ }, false);
505
+ if (selectedOptions.length === 0) {
506
+ this.main.values.innerHTML = this.placeholder().outerHTML;
507
+ return;
508
+ }
509
+ else {
510
+ const placeholder = this.main.values.querySelector('.' + this.classes.placeholder);
511
+ if (placeholder) {
512
+ placeholder.remove();
513
+ }
514
+ }
515
+ let removeNodes = [];
516
+ for (let i = 0; i < currentNodes.length; i++) {
517
+ const node = currentNodes[i];
518
+ const id = node.getAttribute('data-id');
519
+ if (id) {
520
+ const found = selectedOptions.filter((opt) => {
521
+ return opt.id === id;
522
+ }, false);
523
+ if (!found.length) {
524
+ removeNodes.push(node);
525
+ }
526
+ }
527
+ }
528
+ for (const n of removeNodes) {
529
+ n.classList.add(this.classes.valueOut);
530
+ setTimeout(() => {
531
+ this.main.values.removeChild(n);
532
+ }, 100);
533
+ }
534
+ currentNodes = this.main.values.childNodes;
535
+ for (let d = 0; d < selectedOptions.length; d++) {
536
+ let shouldAdd = true;
537
+ for (let i = 0; i < currentNodes.length; i++) {
538
+ if (selectedOptions[d].id === String(currentNodes[i].dataset.id)) {
539
+ shouldAdd = false;
540
+ }
541
+ }
542
+ if (shouldAdd) {
543
+ if (currentNodes.length === 0) {
544
+ this.main.values.appendChild(this.multipleValue(selectedOptions[d]));
545
+ }
546
+ else if (d === 0) {
547
+ this.main.values.insertBefore(this.multipleValue(selectedOptions[d]), currentNodes[d]);
548
+ }
549
+ else {
550
+ currentNodes[d - 1].insertAdjacentElement('afterend', this.multipleValue(selectedOptions[d]));
551
+ }
552
+ }
553
+ }
554
+ }
555
+ multipleValue(option) {
556
+ const value = document.createElement('div');
557
+ value.classList.add(this.classes.value);
558
+ value.dataset.id = option.id;
559
+ const text = document.createElement('div');
560
+ text.classList.add(this.classes.valueText);
561
+ text.innerHTML = option.text;
562
+ value.appendChild(text);
563
+ if (!option.mandatory) {
564
+ const deleteDiv = document.createElement('div');
565
+ deleteDiv.classList.add(this.classes.valueDelete);
566
+ deleteDiv.onclick = (e) => {
567
+ e.preventDefault();
568
+ e.stopPropagation();
569
+ let shouldDelete = true;
570
+ const before = this.store.getSelectedOptions();
571
+ const after = before.filter((o) => {
572
+ return o.selected && o.id !== option.id;
573
+ }, true);
574
+ if (this.settings.minSelected && after.length < this.settings.minSelected) {
575
+ return;
576
+ }
577
+ if (this.callbacks.beforeChange) {
578
+ shouldDelete = this.callbacks.beforeChange(after, before) === true;
579
+ }
580
+ if (shouldDelete) {
581
+ let selectedValues = [];
582
+ for (const o of after) {
583
+ if (o instanceof Optgroup) {
584
+ for (const c of o.options) {
585
+ selectedValues.push(c.value);
586
+ }
587
+ }
588
+ if (o instanceof Option) {
589
+ selectedValues.push(o.value);
590
+ }
591
+ }
592
+ this.callbacks.setSelected(selectedValues);
593
+ if (this.settings.closeOnSelect) {
594
+ this.callbacks.close();
595
+ }
596
+ if (this.callbacks.afterChange) {
597
+ this.callbacks.afterChange(after);
598
+ }
599
+ }
600
+ };
601
+ const deleteSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
602
+ deleteSvg.setAttribute('viewBox', '0 0 100 100');
603
+ const deletePath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
604
+ deletePath.setAttribute('d', this.classes.optionDelete);
605
+ deleteSvg.appendChild(deletePath);
606
+ deleteDiv.appendChild(deleteSvg);
607
+ value.appendChild(deleteDiv);
608
+ }
609
+ return value;
610
+ }
611
+ contentDiv() {
612
+ const main = document.createElement('div');
613
+ main.classList.add(this.classes.content);
614
+ main.dataset.id = this.settings.id;
615
+ if (this.settings.style !== '') {
616
+ main.style.cssText = this.settings.style;
617
+ }
618
+ if (this.settings.contentPosition === 'relative') {
619
+ main.classList.add('ss-' + this.settings.contentPosition);
620
+ }
621
+ if (this.settings.class.length) {
622
+ for (const c of this.settings.class) {
623
+ if (c.trim() !== '') {
624
+ main.classList.add(c.trim());
625
+ }
626
+ }
627
+ }
628
+ const search = this.searchDiv();
629
+ main.appendChild(search.main);
630
+ const list = this.listDiv();
631
+ main.appendChild(list);
632
+ return {
633
+ main: main,
634
+ search: search,
635
+ list: list,
636
+ };
637
+ }
638
+ moveContent() {
639
+ if (this.settings.contentPosition === 'relative') {
640
+ return;
641
+ }
642
+ const containerRect = this.main.main.getBoundingClientRect();
643
+ this.content.main.style.top = containerRect.top + containerRect.height + window.scrollY + 'px';
644
+ this.content.main.style.left = containerRect.left + window.scrollX + 'px';
645
+ this.content.main.style.width = containerRect.width + 'px';
646
+ }
647
+ searchDiv() {
648
+ const main = document.createElement('div');
649
+ const input = document.createElement('input');
650
+ const addable = document.createElement('div');
651
+ main.classList.add(this.classes.search);
652
+ const searchReturn = {
653
+ main,
654
+ input,
655
+ };
656
+ if (!this.settings.showSearch) {
657
+ main.classList.add(this.classes.hide);
658
+ input.readOnly = true;
659
+ }
660
+ input.type = 'search';
661
+ input.placeholder = this.settings.searchPlaceholder;
662
+ input.tabIndex = -1;
663
+ input.setAttribute('aria-label', this.settings.searchPlaceholder);
664
+ input.setAttribute('autocapitalize', 'off');
665
+ input.setAttribute('autocomplete', 'off');
666
+ input.setAttribute('autocorrect', 'off');
667
+ input.oninput = debounce((e) => {
668
+ this.callbacks.search(e.target.value);
669
+ }, 100);
670
+ input.onkeydown = (e) => {
671
+ switch (e.key) {
672
+ case 'ArrowUp':
673
+ case 'ArrowDown':
674
+ this.callbacks.open();
675
+ e.key === 'ArrowDown' ? this.highlight('down') : this.highlight('up');
676
+ return false;
677
+ case 'Tab':
678
+ this.callbacks.close();
679
+ return true;
680
+ case 'Escape':
681
+ this.callbacks.close();
682
+ return false;
683
+ case 'Enter':
684
+ if (this.callbacks.addable && e.ctrlKey) {
685
+ addable.click();
686
+ }
687
+ else {
688
+ const highlighted = this.content.list.querySelector('.' + this.classes.highlighted);
689
+ if (highlighted) {
690
+ highlighted.click();
691
+ }
692
+ }
693
+ return false;
694
+ }
695
+ };
696
+ input.onfocus = () => {
697
+ if (this.settings.isOpen) {
698
+ return;
699
+ }
700
+ this.callbacks.open();
701
+ };
702
+ main.appendChild(input);
703
+ if (this.callbacks.addable) {
704
+ addable.classList.add(this.classes.addable);
705
+ const plus = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
706
+ plus.setAttribute('viewBox', '0 0 100 100');
707
+ const plusPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
708
+ plusPath.setAttribute('d', this.classes.addablePath);
709
+ plus.appendChild(plusPath);
710
+ addable.appendChild(plus);
711
+ addable.onclick = (e) => {
712
+ e.preventDefault();
713
+ e.stopPropagation();
714
+ if (!this.callbacks.addable) {
715
+ return;
716
+ }
717
+ const inputValue = this.content.search.input.value.trim();
718
+ if (inputValue === '') {
719
+ this.content.search.input.focus();
720
+ return;
721
+ }
722
+ const runFinish = (oo) => {
723
+ let newOption = new Option(oo);
724
+ this.callbacks.addOption(newOption);
725
+ if (this.settings.isMultiple) {
726
+ let values = this.store.getSelected();
727
+ values.push(newOption.value);
728
+ this.callbacks.setSelected(values);
729
+ }
730
+ else {
731
+ this.callbacks.setSelected([newOption.value]);
732
+ }
733
+ this.callbacks.search('');
734
+ if (this.settings.closeOnSelect) {
735
+ setTimeout(() => {
736
+ this.callbacks.close();
737
+ }, 100);
738
+ }
739
+ };
740
+ const addableValue = this.callbacks.addable(inputValue);
741
+ if (addableValue instanceof Promise) {
742
+ addableValue.then((value) => {
743
+ if (typeof value === 'string') {
744
+ runFinish({
745
+ text: value,
746
+ value: value,
747
+ });
748
+ }
749
+ else {
750
+ runFinish(value);
751
+ }
752
+ });
753
+ }
754
+ else if (typeof addableValue === 'string') {
755
+ runFinish({
756
+ text: addableValue,
757
+ value: addableValue,
758
+ });
759
+ }
760
+ else {
761
+ runFinish(addableValue);
762
+ }
763
+ return;
764
+ };
765
+ main.appendChild(addable);
766
+ searchReturn.addable = {
767
+ main: addable,
768
+ svg: plus,
769
+ path: plusPath,
770
+ };
771
+ }
772
+ return searchReturn;
773
+ }
774
+ searchFocus(trigger) {
775
+ if (!trigger) {
776
+ this.settings.triggerFocus = false;
777
+ }
778
+ this.content.search.input.focus();
779
+ this.settings.triggerFocus = true;
780
+ }
781
+ getOptions(notPlaceholder = false, notDisabled = false, notHidden = false) {
782
+ let query = '.' + this.classes.option;
783
+ if (notPlaceholder) {
784
+ query += ':not(.' + this.classes.placeholder + ')';
785
+ }
786
+ if (notDisabled) {
787
+ query += ':not(.' + this.classes.disabled + ')';
788
+ }
789
+ if (notHidden) {
790
+ query += ':not(.' + this.classes.hide + ')';
791
+ }
792
+ return Array.from(this.content.list.querySelectorAll(query));
793
+ }
794
+ highlight(dir) {
795
+ const options = this.getOptions(true, true, true);
796
+ if (options.length === 0) {
797
+ return;
798
+ }
799
+ if (options.length === 1) {
800
+ if (!options[0].classList.contains(this.classes.highlighted)) {
801
+ options[0].classList.add(this.classes.highlighted);
802
+ return;
803
+ }
804
+ }
805
+ for (let i = 0; i < options.length; i++) {
806
+ if (options[i].classList.contains(this.classes.highlighted)) {
807
+ options[i].classList.remove(this.classes.highlighted);
808
+ if (dir === 'down') {
809
+ if (i + 1 < options.length) {
810
+ options[i + 1].classList.add(this.classes.highlighted);
811
+ this.ensureElementInView(this.content.list, options[i + 1]);
812
+ }
813
+ else {
814
+ options[0].classList.add(this.classes.highlighted);
815
+ this.ensureElementInView(this.content.list, options[0]);
816
+ }
817
+ }
818
+ else {
819
+ if (i - 1 >= 0) {
820
+ options[i - 1].classList.add(this.classes.highlighted);
821
+ this.ensureElementInView(this.content.list, options[i - 1]);
822
+ }
823
+ else {
824
+ options[options.length - 1].classList.add(this.classes.highlighted);
825
+ this.ensureElementInView(this.content.list, options[options.length - 1]);
826
+ }
827
+ }
828
+ return;
829
+ }
830
+ }
831
+ options[dir === 'down' ? 0 : options.length - 1].classList.add(this.classes.highlighted);
832
+ this.ensureElementInView(this.content.list, options[dir === 'down' ? 0 : options.length - 1]);
833
+ }
834
+ listDiv() {
835
+ const options = document.createElement('div');
836
+ options.classList.add(this.classes.list);
837
+ options.setAttribute('role', 'listbox');
838
+ return options;
839
+ }
840
+ renderError(error) {
841
+ this.content.list.innerHTML = '';
842
+ const errorDiv = document.createElement('div');
843
+ errorDiv.classList.add(this.classes.error);
844
+ errorDiv.textContent = error;
845
+ this.content.list.appendChild(errorDiv);
846
+ }
847
+ renderSearching() {
848
+ this.content.list.innerHTML = '';
849
+ const searchingDiv = document.createElement('div');
850
+ searchingDiv.classList.add(this.classes.searching);
851
+ searchingDiv.textContent = this.settings.searchingText;
852
+ this.content.list.appendChild(searchingDiv);
853
+ }
854
+ renderOptions(data) {
855
+ this.content.list.innerHTML = '';
856
+ if (data.length === 0) {
857
+ const noResults = document.createElement('div');
858
+ noResults.classList.add(this.classes.option);
859
+ noResults.classList.add(this.classes.disabled);
860
+ noResults.innerHTML = this.settings.searchText;
861
+ this.content.list.appendChild(noResults);
862
+ return;
863
+ }
864
+ for (const d of data) {
865
+ if (d instanceof Optgroup) {
866
+ const optgroupEl = document.createElement('div');
867
+ optgroupEl.classList.add(this.classes.optgroup);
868
+ const optgroupLabel = document.createElement('div');
869
+ optgroupLabel.classList.add(this.classes.optgroupLabel);
870
+ optgroupLabel.innerHTML = d.label;
871
+ if (this.settings.selectByGroup && this.settings.isMultiple) {
872
+ optgroupLabel.classList.add(this.classes.optgroupSelectable);
873
+ optgroupLabel.addEventListener('click', (e) => {
874
+ e.preventDefault();
875
+ e.stopPropagation();
876
+ for (const childEl of optgroupEl.children) {
877
+ if (childEl.className.indexOf(this.classes.option) !== -1) {
878
+ childEl.click();
879
+ }
880
+ }
881
+ });
882
+ }
883
+ optgroupEl.appendChild(optgroupLabel);
884
+ for (const o of d.options) {
885
+ optgroupEl.appendChild(this.option(o));
886
+ }
887
+ this.content.list.appendChild(optgroupEl);
888
+ }
889
+ if (d instanceof Option) {
890
+ this.content.list.appendChild(this.option(d));
891
+ }
892
+ }
893
+ }
894
+ option(option) {
895
+ if (option.placeholder) {
896
+ const placeholder = document.createElement('div');
897
+ placeholder.classList.add(this.classes.option);
898
+ placeholder.classList.add(this.classes.hide);
899
+ return placeholder;
900
+ }
901
+ const optionEl = document.createElement('div');
902
+ optionEl.dataset.id = option.id;
903
+ optionEl.classList.add(this.classes.option);
904
+ optionEl.setAttribute('role', 'option');
905
+ if (option.class) {
906
+ option.class.split(' ').forEach((dataClass) => {
907
+ optionEl.classList.add(dataClass);
908
+ });
909
+ }
910
+ if (option.style) {
911
+ optionEl.style.cssText = option.style;
912
+ }
913
+ if (this.settings.searchHighlight && this.content.search.input.value.trim() !== '') {
914
+ const textOrHtml = option.html !== '' ? option.html : option.text;
915
+ optionEl.innerHTML = this.highlightText(textOrHtml, this.content.search.input.value, this.classes.searchHighlighter);
916
+ }
917
+ else if (option.html !== '') {
918
+ optionEl.innerHTML = option.html;
919
+ }
920
+ else {
921
+ optionEl.textContent = option.text;
922
+ }
923
+ if (this.settings.showOptionTooltips && optionEl.textContent) {
924
+ optionEl.setAttribute('title', optionEl.textContent);
925
+ }
926
+ if ((option.selected && !this.settings.allowDeselect) || (option.disabled && !this.settings.allowDeselect)) {
927
+ optionEl.classList.add(this.classes.disabled);
928
+ }
929
+ if (option.selected && this.settings.hideSelected) {
930
+ optionEl.classList.add(this.classes.hide);
931
+ }
932
+ if (option.selected) {
933
+ optionEl.classList.add(this.classes.optionSelected);
934
+ }
935
+ else {
936
+ optionEl.classList.remove(this.classes.optionSelected);
937
+ }
938
+ optionEl.addEventListener('click', (e) => {
939
+ e.preventDefault();
940
+ e.stopPropagation();
941
+ const selectedOptions = this.store.getSelected();
942
+ const element = e.currentTarget;
943
+ const elementID = String(element.dataset.id);
944
+ if (option.disabled || (option.selected && !this.settings.allowDeselect)) {
945
+ return;
946
+ }
947
+ if (this.settings.isMultiple &&
948
+ Array.isArray(selectedOptions) &&
949
+ this.settings.maxSelected <= selectedOptions.length) {
950
+ return;
951
+ }
952
+ let shouldUpdate = false;
953
+ const before = this.store.getSelectedOptions();
954
+ let after = [];
955
+ if (this.settings.isMultiple) {
956
+ if (option.selected) {
957
+ after = before.filter((o) => o.id !== elementID);
958
+ }
959
+ else {
960
+ after = before.concat(option);
961
+ }
962
+ }
963
+ if (!this.settings.isMultiple) {
964
+ if (option.selected) {
965
+ after = [];
966
+ }
967
+ else {
968
+ after = [option];
969
+ }
970
+ }
971
+ if (!this.callbacks.beforeChange) {
972
+ shouldUpdate = true;
973
+ }
974
+ if (this.callbacks.beforeChange) {
975
+ if (this.callbacks.beforeChange(after, before) === false) {
976
+ shouldUpdate = false;
977
+ }
978
+ else {
979
+ shouldUpdate = true;
980
+ }
981
+ }
982
+ if (shouldUpdate) {
983
+ if (!this.store.getOptionByID(elementID)) {
984
+ this.callbacks.addOption(option);
985
+ }
986
+ this.callbacks.setSelected(after.map((o) => o.value));
987
+ if (this.settings.closeOnSelect) {
988
+ this.callbacks.close();
989
+ }
990
+ if (this.callbacks.afterChange) {
991
+ this.callbacks.afterChange(after);
992
+ }
993
+ }
994
+ });
995
+ return optionEl;
996
+ }
997
+ destroy() {
998
+ this.main.main.remove();
999
+ this.content.main.remove();
1000
+ }
1001
+ highlightText(str, search, className) {
1002
+ let completedString = str;
1003
+ const regex = new RegExp('(' + search.trim() + ')(?![^<]*>[^<>]*</)', 'i');
1004
+ if (!str.match(regex)) {
1005
+ return str;
1006
+ }
1007
+ const matchStartPosition = str.match(regex).index;
1008
+ const matchEndPosition = matchStartPosition + str.match(regex)[0].toString().length;
1009
+ const originalTextFoundByRegex = str.substring(matchStartPosition, matchEndPosition);
1010
+ completedString = completedString.replace(regex, `<mark class="${className}">${originalTextFoundByRegex}</mark>`);
1011
+ return completedString;
1012
+ }
1013
+ moveContentAbove() {
1014
+ let mainHeight = this.main.main.offsetHeight;
1015
+ const contentHeight = this.content.main.offsetHeight;
1016
+ const height = mainHeight + contentHeight - 1;
1017
+ this.content.main.style.margin = '-' + height + 'px 0px 0px 0px';
1018
+ this.content.main.style.transformOrigin = 'center bottom';
1019
+ this.main.main.classList.remove(this.classes.openBelow);
1020
+ this.main.main.classList.add(this.classes.openAbove);
1021
+ this.content.main.classList.remove(this.classes.openBelow);
1022
+ this.content.main.classList.add(this.classes.openAbove);
1023
+ }
1024
+ moveContentBelow() {
1025
+ this.content.main.style.margin = '-1px 0px 0px 0px';
1026
+ this.content.main.style.transformOrigin = 'center top';
1027
+ this.main.main.classList.remove(this.classes.openAbove);
1028
+ this.main.main.classList.add(this.classes.openBelow);
1029
+ this.content.main.classList.remove(this.classes.openAbove);
1030
+ this.content.main.classList.add(this.classes.openBelow);
1031
+ }
1032
+ ensureElementInView(container, element) {
1033
+ const cTop = container.scrollTop + container.offsetTop;
1034
+ const cBottom = cTop + container.clientHeight;
1035
+ const eTop = element.offsetTop;
1036
+ const eBottom = eTop + element.clientHeight;
1037
+ if (eTop < cTop) {
1038
+ container.scrollTop -= cTop - eTop;
1039
+ }
1040
+ else if (eBottom > cBottom) {
1041
+ container.scrollTop += eBottom - cBottom;
1042
+ }
1043
+ }
1044
+ putContent(el, isOpen) {
1045
+ const height = el.offsetHeight;
1046
+ const rect = el.getBoundingClientRect();
1047
+ const elemTop = isOpen ? rect.top : rect.top - height;
1048
+ const elemBottom = isOpen ? rect.bottom : rect.bottom + height;
1049
+ if (elemTop <= 0) {
1050
+ return 'down';
1051
+ }
1052
+ if (elemBottom >= window.innerHeight) {
1053
+ return 'up';
1054
+ }
1055
+ return 'down';
1056
+ }
1057
+ }
1058
+
1059
+ class Select {
1060
+ constructor(select) {
1061
+ this.listen = false;
1062
+ this.observer = null;
1063
+ this.select = select;
1064
+ }
1065
+ enable() {
1066
+ this.disconnectObserver();
1067
+ this.select.disabled = false;
1068
+ this.connectObserver();
1069
+ }
1070
+ disable() {
1071
+ this.disconnectObserver();
1072
+ this.select.disabled = true;
1073
+ this.connectObserver();
1074
+ }
1075
+ hideUI() {
1076
+ this.select.tabIndex = -1;
1077
+ this.select.style.display = 'none';
1078
+ this.select.setAttribute('aria-hidden', 'true');
1079
+ }
1080
+ showUI() {
1081
+ this.select.removeAttribute('tabindex');
1082
+ this.select.style.display = '';
1083
+ this.select.removeAttribute('aria-hidden');
1084
+ }
1085
+ changeListen(on) {
1086
+ this.listen = on;
1087
+ if (this.listen) {
1088
+ this.connectObserver();
1089
+ }
1090
+ else {
1091
+ this.disconnectObserver();
1092
+ }
1093
+ }
1094
+ addSelectChangeListener(func) {
1095
+ this.onSelectChange = func;
1096
+ this.addObserver();
1097
+ this.connectObserver();
1098
+ this.changeListen(true);
1099
+ }
1100
+ removeSelectChangeListener() {
1101
+ this.changeListen(false);
1102
+ this.onSelectChange = undefined;
1103
+ }
1104
+ addValueChangeListener(func) {
1105
+ this.onValueChange = func;
1106
+ this.select.addEventListener('change', this.valueChange.bind(this));
1107
+ }
1108
+ removeValueChangeListener() {
1109
+ this.onValueChange = undefined;
1110
+ this.select.removeEventListener('change', this.valueChange.bind(this));
1111
+ }
1112
+ valueChange(ev) {
1113
+ if (this.onValueChange) {
1114
+ this.onValueChange(this.getSelectedValues());
1115
+ }
1116
+ }
1117
+ observeWrapper(mutations) {
1118
+ if (this.onSelectChange) {
1119
+ this.changeListen(false);
1120
+ this.onSelectChange(this.getData());
1121
+ this.changeListen(true);
1122
+ }
1123
+ }
1124
+ addObserver() {
1125
+ if (this.observer) {
1126
+ this.disconnectObserver();
1127
+ this.observer = null;
1128
+ }
1129
+ this.observer = new MutationObserver(this.observeWrapper.bind(this));
1130
+ }
1131
+ connectObserver() {
1132
+ if (this.observer) {
1133
+ this.observer.observe(this.select, {
1134
+ childList: true,
1135
+ });
1136
+ }
1137
+ }
1138
+ disconnectObserver() {
1139
+ if (this.observer) {
1140
+ this.observer.disconnect();
1141
+ }
1142
+ }
1143
+ getData() {
1144
+ let data = [];
1145
+ const nodes = this.select.childNodes;
1146
+ for (const n of nodes) {
1147
+ if (n.nodeName === 'OPTGROUP') {
1148
+ data.push(this.getDataFromOptgroup(n));
1149
+ }
1150
+ if (n.nodeName === 'OPTION') {
1151
+ data.push(this.getDataFromOption(n));
1152
+ }
1153
+ }
1154
+ return data;
1155
+ }
1156
+ getDataFromOptgroup(optgroup) {
1157
+ let data = {
1158
+ id: '',
1159
+ label: optgroup.label,
1160
+ options: [],
1161
+ };
1162
+ const options = optgroup.childNodes;
1163
+ for (const o of options) {
1164
+ if (o.nodeName === 'OPTION') {
1165
+ data.options.push(this.getDataFromOption(o));
1166
+ }
1167
+ }
1168
+ return data;
1169
+ }
1170
+ getSelectedValues() {
1171
+ let values = [];
1172
+ const options = this.select.childNodes;
1173
+ for (const o of options) {
1174
+ if (o.nodeName === 'OPTGROUP') {
1175
+ const optgroupOptions = o.childNodes;
1176
+ for (const oo of optgroupOptions) {
1177
+ if (oo.nodeName === 'OPTION') {
1178
+ const option = oo;
1179
+ if (option.selected) {
1180
+ values.push(option.value);
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ if (o.nodeName === 'OPTION') {
1186
+ const option = o;
1187
+ if (option.selected) {
1188
+ values.push(option.value);
1189
+ }
1190
+ }
1191
+ }
1192
+ return values;
1193
+ }
1194
+ getDataFromOption(option) {
1195
+ return {
1196
+ id: (option.dataset ? option.dataset.id : false) || generateID(),
1197
+ value: option.value,
1198
+ text: option.text,
1199
+ html: option.innerHTML,
1200
+ selected: option.selected,
1201
+ display: option.style.display === 'none' ? false : true,
1202
+ disabled: option.disabled,
1203
+ mandatory: option.dataset ? option.dataset.mandatory === 'true' : false,
1204
+ placeholder: option.dataset.placeholder === 'true',
1205
+ class: option.className,
1206
+ style: option.style.cssText,
1207
+ data: option.dataset,
1208
+ };
1209
+ }
1210
+ setSelected(value) {
1211
+ const options = this.select.childNodes;
1212
+ for (const o of options) {
1213
+ if (o.nodeName === 'OPTGROUP') {
1214
+ const optgroup = o;
1215
+ const optgroupOptions = optgroup.childNodes;
1216
+ for (const oo of optgroupOptions) {
1217
+ if (oo.nodeName === 'OPTION') {
1218
+ const option = oo;
1219
+ option.selected = value.includes(option.value);
1220
+ }
1221
+ }
1222
+ }
1223
+ if (o.nodeName === 'OPTION') {
1224
+ const option = o;
1225
+ option.selected = value.includes(option.value);
1226
+ }
1227
+ }
1228
+ }
1229
+ updateSelect(id, style, classes) {
1230
+ this.changeListen(false);
1231
+ if (id) {
1232
+ this.select.id = id;
1233
+ }
1234
+ if (style) {
1235
+ this.select.style.cssText = style;
1236
+ }
1237
+ if (classes) {
1238
+ this.select.className = '';
1239
+ classes.forEach((c) => {
1240
+ if (c.trim() !== '') {
1241
+ this.select.classList.add(c.trim());
1242
+ }
1243
+ });
1244
+ }
1245
+ this.changeListen(true);
1246
+ }
1247
+ updateOptions(data) {
1248
+ this.changeListen(false);
1249
+ this.select.innerHTML = '';
1250
+ for (const d of data) {
1251
+ if (d instanceof Optgroup) {
1252
+ this.select.appendChild(this.createOptgroup(d));
1253
+ }
1254
+ if (d instanceof Option) {
1255
+ this.select.appendChild(this.createOption(d));
1256
+ }
1257
+ }
1258
+ this.changeListen(true);
1259
+ }
1260
+ createOptgroup(optgroup) {
1261
+ const optgroupEl = document.createElement('optgroup');
1262
+ optgroupEl.id = optgroup.id;
1263
+ optgroupEl.label = optgroup.label;
1264
+ if (optgroup.options) {
1265
+ for (const o of optgroup.options) {
1266
+ optgroupEl.appendChild(this.createOption(o));
1267
+ }
1268
+ }
1269
+ return optgroupEl;
1270
+ }
1271
+ createOption(info) {
1272
+ const optionEl = document.createElement('option');
1273
+ optionEl.value = info.value !== '' ? info.value : info.text;
1274
+ optionEl.innerHTML = info.html || info.text;
1275
+ if (info.selected) {
1276
+ optionEl.selected = info.selected;
1277
+ }
1278
+ if (info.disabled) {
1279
+ optionEl.disabled = true;
1280
+ }
1281
+ if (info.display === false) {
1282
+ optionEl.style.display = 'none';
1283
+ }
1284
+ if (info.placeholder) {
1285
+ optionEl.setAttribute('data-placeholder', 'true');
1286
+ }
1287
+ if (info.mandatory) {
1288
+ optionEl.setAttribute('data-mandatory', 'true');
1289
+ }
1290
+ if (info.class) {
1291
+ info.class.split(' ').forEach((optionClass) => {
1292
+ optionEl.classList.add(optionClass);
1293
+ });
1294
+ }
1295
+ if (info.data && typeof info.data === 'object') {
1296
+ Object.keys(info.data).forEach((key) => {
1297
+ optionEl.setAttribute('data-' + kebabCase(key), info.data[key]);
1298
+ });
1299
+ }
1300
+ return optionEl;
1301
+ }
1302
+ destroy() {
1303
+ this.changeListen(false);
1304
+ this.disconnectObserver();
1305
+ this.removeSelectChangeListener();
1306
+ this.removeValueChangeListener();
1307
+ this.showUI();
1308
+ }
1309
+ }
1310
+
1311
+ class Settings {
1312
+ constructor(settings) {
1313
+ this.id = '';
1314
+ this.style = '';
1315
+ this.class = [];
1316
+ this.isMultiple = false;
1317
+ this.isOpen = false;
1318
+ this.triggerFocus = true;
1319
+ this.intervalMove = null;
1320
+ this.mainHeight = 30;
1321
+ this.contentHeight = 0;
1322
+ if (!settings) {
1323
+ settings = {};
1324
+ }
1325
+ this.id = 'ss-' + generateID();
1326
+ this.style = settings.style || '';
1327
+ this.class = settings.class || [];
1328
+ this.isEnabled = settings.isEnabled !== undefined ? settings.isEnabled : true;
1329
+ this.alwaysOpen = settings.alwaysOpen !== undefined ? settings.alwaysOpen : false;
1330
+ this.showSearch = settings.showSearch !== undefined ? settings.showSearch : true;
1331
+ this.searchPlaceholder = settings.searchPlaceholder || 'Search';
1332
+ this.searchText = settings.searchText || 'No Results';
1333
+ this.searchingText = settings.searchingText || 'Searching...';
1334
+ this.searchHighlight = settings.searchHighlight !== undefined ? settings.searchHighlight : false;
1335
+ this.closeOnSelect = settings.closeOnSelect !== undefined ? settings.closeOnSelect : true;
1336
+ this.contentLocation = settings.contentLocation || document.body;
1337
+ this.contentPosition = settings.contentPosition || 'absolute';
1338
+ this.openPosition = settings.openPosition || 'auto';
1339
+ this.placeholderText = settings.placeholderText || 'Select Value';
1340
+ this.allowDeselect = settings.allowDeselect !== undefined ? settings.allowDeselect : false;
1341
+ this.hideSelected = settings.hideSelected !== undefined ? settings.hideSelected : false;
1342
+ this.showOptionTooltips = settings.showOptionTooltips !== undefined ? settings.showOptionTooltips : false;
1343
+ this.selectByGroup = settings.selectByGroup !== undefined ? settings.selectByGroup : false;
1344
+ this.minSelected = settings.minSelected || 0;
1345
+ this.maxSelected = settings.maxSelected || 1000;
1346
+ this.timeoutDelay = settings.timeoutDelay || 200;
1347
+ }
1348
+ }
1349
+
1350
+ class SlimSelect {
1351
+ constructor(config) {
1352
+ this.events = {
1353
+ search: undefined,
1354
+ searchFilter: (opt, search) => {
1355
+ return opt.text.toLowerCase().indexOf(search.toLowerCase()) !== -1;
1356
+ },
1357
+ addable: undefined,
1358
+ beforeChange: undefined,
1359
+ afterChange: undefined,
1360
+ beforeOpen: undefined,
1361
+ afterOpen: undefined,
1362
+ beforeClose: undefined,
1363
+ afterClose: undefined,
1364
+ };
1365
+ this.windowResize = debounce(() => {
1366
+ if (!this.settings.isOpen) {
1367
+ return;
1368
+ }
1369
+ this.render.moveContent();
1370
+ });
1371
+ this.windowScroll = debounce(() => {
1372
+ if (!this.settings.isOpen) {
1373
+ return;
1374
+ }
1375
+ if (this.settings.openPosition === 'down') {
1376
+ this.render.moveContentBelow();
1377
+ return;
1378
+ }
1379
+ else if (this.settings.openPosition === 'up') {
1380
+ this.render.moveContentAbove();
1381
+ return;
1382
+ }
1383
+ if (this.settings.contentPosition === 'relative') {
1384
+ this.render.moveContentBelow();
1385
+ }
1386
+ else if (this.render.putContent(this.render.content.main, this.settings.isOpen) === 'up') {
1387
+ this.render.moveContentAbove();
1388
+ }
1389
+ else {
1390
+ this.render.moveContentBelow();
1391
+ }
1392
+ });
1393
+ this.documentClick = (e) => {
1394
+ if (!this.settings.isOpen) {
1395
+ return;
1396
+ }
1397
+ if (e.target && !hasClassInTree(e.target, this.settings.id)) {
1398
+ this.close();
1399
+ }
1400
+ };
1401
+ this.selectEl = (typeof config.select === 'string' ? document.querySelector(config.select) : config.select);
1402
+ if (!this.selectEl) {
1403
+ if (config.events && config.events.error) {
1404
+ config.events.error(new Error('Could not find select element'));
1405
+ }
1406
+ return;
1407
+ }
1408
+ if (this.selectEl.tagName !== 'SELECT') {
1409
+ if (config.events && config.events.error) {
1410
+ config.events.error(new Error('Element isnt of type select'));
1411
+ }
1412
+ return;
1413
+ }
1414
+ if (this.selectEl.dataset.ssid) {
1415
+ this.destroy();
1416
+ }
1417
+ this.settings = new Settings(config.settings);
1418
+ for (const key in config.events) {
1419
+ if (config.events.hasOwnProperty(key)) {
1420
+ this.events[key] = config.events[key];
1421
+ }
1422
+ }
1423
+ this.settings.isMultiple = this.selectEl.multiple;
1424
+ this.settings.style = this.selectEl.style.cssText;
1425
+ this.settings.class = this.selectEl.className.split(' ');
1426
+ this.select = new Select(this.selectEl);
1427
+ this.select.updateSelect(this.settings.id, this.settings.style, this.settings.class);
1428
+ this.select.hideUI();
1429
+ this.select.addSelectChangeListener((data) => {
1430
+ this.setData(data);
1431
+ });
1432
+ this.select.addValueChangeListener((values) => {
1433
+ this.setSelected(values);
1434
+ });
1435
+ this.store = new Store(this.settings.isMultiple ? 'multiple' : 'single', config.data ? config.data : this.select.getData());
1436
+ if (config.data) {
1437
+ this.select.updateOptions(this.store.getData());
1438
+ }
1439
+ const callbacks = {
1440
+ open: this.open.bind(this),
1441
+ close: this.close.bind(this),
1442
+ addable: this.events.addable ? this.events.addable : undefined,
1443
+ setSelected: this.setSelected.bind(this),
1444
+ addOption: this.addOption.bind(this),
1445
+ search: this.search.bind(this),
1446
+ beforeChange: this.events.beforeChange,
1447
+ afterChange: this.events.afterChange,
1448
+ };
1449
+ this.render = new Render(this.settings, this.store, callbacks);
1450
+ if (this.selectEl.parentNode) {
1451
+ this.selectEl.parentNode.insertBefore(this.render.main.main, this.selectEl.nextSibling);
1452
+ }
1453
+ document.addEventListener('click', this.documentClick);
1454
+ window.addEventListener('resize', this.windowResize, false);
1455
+ if (this.settings.openPosition === 'auto') {
1456
+ window.addEventListener('scroll', this.windowScroll, false);
1457
+ }
1458
+ if (!this.settings.isEnabled) {
1459
+ this.disable();
1460
+ }
1461
+ if (this.settings.alwaysOpen) {
1462
+ this.open();
1463
+ }
1464
+ this.selectEl.slim = this;
1465
+ }
1466
+ enable() {
1467
+ this.settings.isEnabled = true;
1468
+ this.select.enable();
1469
+ this.render.enable();
1470
+ }
1471
+ disable() {
1472
+ this.settings.isEnabled = false;
1473
+ this.select.disable();
1474
+ this.render.disable();
1475
+ }
1476
+ getData() {
1477
+ return this.store.getData();
1478
+ }
1479
+ setData(data) {
1480
+ const err = this.store.validateDataArray(data);
1481
+ if (err) {
1482
+ if (this.events.error) {
1483
+ this.events.error(err);
1484
+ }
1485
+ return;
1486
+ }
1487
+ this.store.setData(data);
1488
+ const dataClean = this.store.getData();
1489
+ this.select.updateOptions(dataClean);
1490
+ this.render.renderValues();
1491
+ this.render.renderOptions(dataClean);
1492
+ }
1493
+ getSelected() {
1494
+ return this.store.getSelected();
1495
+ }
1496
+ setSelected(value) {
1497
+ this.store.setSelectedBy('value', Array.isArray(value) ? value : [value]);
1498
+ const data = this.store.getData();
1499
+ this.select.updateOptions(data);
1500
+ this.render.renderValues();
1501
+ this.render.renderOptions(data);
1502
+ }
1503
+ addOption(option) {
1504
+ this.store.addOption(option);
1505
+ const data = this.store.getData();
1506
+ this.select.updateOptions(data);
1507
+ this.render.renderValues();
1508
+ this.render.renderOptions(data);
1509
+ }
1510
+ open() {
1511
+ if (!this.settings.isEnabled || this.settings.isOpen) {
1512
+ return;
1513
+ }
1514
+ if (this.events.beforeOpen) {
1515
+ this.events.beforeOpen();
1516
+ }
1517
+ this.render.open();
1518
+ if (this.settings.showSearch) {
1519
+ this.render.searchFocus(false);
1520
+ }
1521
+ setTimeout(() => {
1522
+ if (this.events.afterOpen) {
1523
+ this.events.afterOpen();
1524
+ }
1525
+ this.settings.isOpen = true;
1526
+ }, this.settings.timeoutDelay);
1527
+ if (this.settings.intervalMove) {
1528
+ clearInterval(this.settings.intervalMove);
1529
+ }
1530
+ this.settings.intervalMove = setInterval(this.render.moveContent.bind(this.render), 500);
1531
+ }
1532
+ close() {
1533
+ if (!this.settings.isOpen || this.settings.alwaysOpen) {
1534
+ return;
1535
+ }
1536
+ if (this.events.beforeClose) {
1537
+ this.events.beforeClose();
1538
+ }
1539
+ this.render.close();
1540
+ this.search('');
1541
+ this.render.mainFocus(false);
1542
+ setTimeout(() => {
1543
+ if (this.events.afterClose) {
1544
+ this.events.afterClose();
1545
+ }
1546
+ this.settings.isOpen = false;
1547
+ }, this.settings.timeoutDelay);
1548
+ if (this.settings.intervalMove) {
1549
+ clearInterval(this.settings.intervalMove);
1550
+ }
1551
+ }
1552
+ search(value) {
1553
+ if (this.render.content.search.input.value !== value) {
1554
+ this.render.content.search.input.value = value;
1555
+ }
1556
+ if (!this.events.search) {
1557
+ this.render.renderOptions(value === '' ? this.store.getData() : this.store.search(value, this.events.searchFilter));
1558
+ return;
1559
+ }
1560
+ this.render.renderSearching();
1561
+ const searchResp = this.events.search(value, this.store.getSelectedOptions());
1562
+ if (searchResp instanceof Promise) {
1563
+ searchResp
1564
+ .then((data) => {
1565
+ this.render.renderOptions(this.store.partialToFullData(data));
1566
+ })
1567
+ .catch((err) => {
1568
+ this.render.renderError(typeof err === 'string' ? err : err.message);
1569
+ });
1570
+ return;
1571
+ }
1572
+ else if (Array.isArray(searchResp)) {
1573
+ this.render.renderOptions(this.store.partialToFullData(searchResp));
1574
+ }
1575
+ else {
1576
+ this.render.renderError('Search event must return a promise or an array of data');
1577
+ }
1578
+ }
1579
+ destroy() {
1580
+ document.removeEventListener('click', this.documentClick);
1581
+ window.removeEventListener('resize', this.windowResize, false);
1582
+ if (this.settings.openPosition === 'auto') {
1583
+ window.removeEventListener('scroll', this.windowScroll, false);
1584
+ }
1585
+ this.store.setData([]);
1586
+ this.render.destroy();
1587
+ this.select.destroy();
1588
+ }
1589
+ }
1590
+
1591
+ var script = vue.defineComponent({
1592
+ name: 'SlimSelect',
1593
+ props: {
1594
+ data: {
1595
+ type: Array,
1596
+ },
1597
+ settings: {
1598
+ type: Object,
1599
+ },
1600
+ events: {
1601
+ type: Object,
1602
+ },
1603
+ },
1604
+ data() {
1605
+ return {
1606
+ slim: null,
1607
+ };
1608
+ },
1609
+ mounted() {
1610
+ let config = {
1611
+ select: this.$refs.slim,
1612
+ };
1613
+ if (this.data) {
1614
+ config.data = this.data;
1615
+ }
1616
+ if (this.settings) {
1617
+ config.settings = this.settings;
1618
+ }
1619
+ if (this.events) {
1620
+ config.events = this.events;
1621
+ }
1622
+ this.slim = new SlimSelect(config);
1623
+ },
1624
+ beforeUnmount() {
1625
+ if (this.slim) {
1626
+ this.slim.destroy();
1627
+ }
1628
+ },
1629
+ watch: {
1630
+ data: {
1631
+ handler: function (newData) {
1632
+ if (this.slim) {
1633
+ this.slim.setData(newData);
1634
+ }
1635
+ },
1636
+ deep: true,
1637
+ },
1638
+ },
1639
+ methods: {
1640
+ getSlimSelect() {
1641
+ return this.slim;
1642
+ },
1643
+ },
1644
+ });
1645
+
1646
+ const _hoisted_1 = { ref: "slim" };
1647
+
1648
+ function render(_ctx, _cache, $props, $setup, $data, $options) {
1649
+ return (vue.openBlock(), vue.createElementBlock("select", _hoisted_1, [
1650
+ vue.renderSlot(_ctx.$slots, "default")
1651
+ ], 512 /* NEED_PATCH */))
1652
+ }
1653
+
1654
+ script.render = render;
1655
+ script.__file = "slimselect.vue";
1656
+
1657
+ module.exports = script;