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