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