slim-select 2.3.2 → 2.4.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.
@@ -127,6 +127,7 @@ export default class Render {
127
127
 
128
128
  // Add classes and styles to main/content
129
129
  this.updateClassStyles()
130
+ this.updateAriaAttributes()
130
131
 
131
132
  // Add content to the content location settings
132
133
  this.settings.contentLocation.appendChild(this.content.main)
@@ -155,6 +156,7 @@ export default class Render {
155
156
 
156
157
  // Add class to main container
157
158
  this.main.main.classList.add(this.settings.openPosition === 'up' ? this.classes.openAbove : this.classes.openBelow)
159
+ this.main.main.setAttribute('aria-expanded', 'true')
158
160
 
159
161
  // move the content in to the right location
160
162
  this.moveContent()
@@ -173,6 +175,7 @@ export default class Render {
173
175
  public close(): void {
174
176
  this.main.main.classList.remove(this.classes.openAbove)
175
177
  this.main.main.classList.remove(this.classes.openBelow)
178
+ this.main.main.setAttribute('aria-expanded', 'false')
176
179
  this.content.main.classList.remove(this.classes.openAbove)
177
180
  this.content.main.classList.remove(this.classes.openBelow)
178
181
  this.main.arrow.path.setAttribute('d', this.classes.arrowClose)
@@ -211,23 +214,26 @@ export default class Render {
211
214
  }
212
215
  }
213
216
 
217
+ public updateAriaAttributes() {
218
+ this.main.main.role = 'combobox'
219
+ this.main.main.setAttribute('aria-haspopup', 'listbox')
220
+ this.main.main.setAttribute('aria-controls', this.content.main.id)
221
+ this.main.main.setAttribute('aria-expanded', 'false')
222
+ this.content.main.setAttribute('role', 'listbox')
223
+ // do an aria-labelledby here maybe
224
+ }
225
+
214
226
  public mainDiv(): Main {
215
227
  // Create main container
216
228
  const main = document.createElement('div')
217
229
 
218
230
  // Add id to data-id
219
231
  main.dataset.id = this.settings.id
232
+ main.id = this.settings.id
220
233
 
221
234
  // Set tabable to allow tabbing to the element
222
235
  main.tabIndex = 0
223
236
 
224
- // If main gets focus, open the content
225
- main.onfocus = () => {
226
- if (this.settings.triggerFocus) {
227
- this.callbacks.open()
228
- }
229
- }
230
-
231
237
  // Deal with keyboard events on the main div
232
238
  // This is to allow for normal selecting
233
239
  // when you may not have a search bar
@@ -243,6 +249,8 @@ export default class Render {
243
249
  this.callbacks.close()
244
250
  return true // Continue doing normal tabbing
245
251
  case 'Enter':
252
+ case ' ':
253
+ this.callbacks.open()
246
254
  const highlighted = this.content.list.querySelector('.' + this.classes.highlighted) as HTMLDivElement
247
255
  if (highlighted) {
248
256
  highlighted.click()
@@ -346,12 +354,17 @@ export default class Render {
346
354
  }
347
355
  }
348
356
 
349
- public mainFocus(trigger: boolean): void {
357
+ public mainFocus(trigger: boolean, eventType: string | null): void {
350
358
  if (!trigger) {
351
359
  this.settings.triggerFocus = false
352
360
  }
353
361
  // Trigger focus but dont scroll to it
354
- this.main.main.focus({ preventScroll: true })
362
+ // Need for prevent refocus the element if event is not keyboard event.
363
+ // For example if event is mouse click or tachpad click this condition prevent refocus on element
364
+ // because click by mouse change focus position and not need return focus to element.
365
+ if (eventType !== 'click') {
366
+ this.main.main.focus({ preventScroll: true })
367
+ }
355
368
  this.settings.triggerFocus = true
356
369
  }
357
370
 
@@ -595,6 +608,7 @@ export default class Render {
595
608
 
596
609
  // Add id to data-id
597
610
  main.dataset.id = this.settings.id
611
+ main.id = this.settings.id
598
612
 
599
613
  // Add search
600
614
  const search = this.searchDiv()
@@ -685,6 +699,7 @@ export default class Render {
685
699
  this.callbacks.close()
686
700
  return false
687
701
  case 'Enter':
702
+ case ' ':
688
703
  if (this.callbacks.addable && e.ctrlKey) {
689
704
  addable.click()
690
705
  } else {
@@ -897,7 +912,6 @@ export default class Render {
897
912
  public listDiv(): HTMLDivElement {
898
913
  const options = document.createElement('div')
899
914
  options.classList.add(this.classes.list)
900
- options.setAttribute('role', 'listbox')
901
915
 
902
916
  return options
903
917
  }
@@ -1117,6 +1131,7 @@ export default class Render {
1117
1131
  // Create option
1118
1132
  const optionEl = document.createElement('div')
1119
1133
  optionEl.dataset.id = option.id // Dataset id for identifying an option
1134
+ optionEl.id = option.id
1120
1135
  optionEl.classList.add(this.classes.option)
1121
1136
  optionEl.setAttribute('role', 'option') // WCAG attribute
1122
1137
  if (option.class) {
@@ -1164,8 +1179,11 @@ export default class Render {
1164
1179
  // If option is selected
1165
1180
  if (option.selected) {
1166
1181
  optionEl.classList.add(this.classes.selected)
1182
+ optionEl.setAttribute('aria-selected', 'true')
1183
+ this.main.main.setAttribute('aria-activedescendant', optionEl.id)
1167
1184
  } else {
1168
1185
  optionEl.classList.remove(this.classes.selected)
1186
+ optionEl.setAttribute('aria-selected', 'false')
1169
1187
  }
1170
1188
 
1171
1189
  // Add click event listener
@@ -10,6 +10,7 @@ export default class Settings {
10
10
  // Dynamic settings
11
11
  public isMultiple: boolean = false
12
12
  public isOpen: boolean = false
13
+ public isWindowFocused: boolean = true
13
14
  public triggerFocus: boolean = true
14
15
  public intervalMove: NodeJS.Timeout | null = null
15
16
 
@@ -68,6 +68,7 @@
68
68
  outline: 0;
69
69
  box-sizing: border-box;
70
70
  transition: background-color var(--ss-animation-timing);
71
+ overflow: hidden;
71
72
 
72
73
  &:focus {
73
74
  box-shadow: 0 0 5px var(--ss-primary-color);
@@ -1,5 +1,5 @@
1
1
  export declare function generateID(): string;
2
2
  export declare function hasClassInTree(element: HTMLElement, className: string): any;
3
- export declare function debounce(func: (...params: any[]) => void, wait?: number, immediate?: boolean): () => void;
3
+ export declare function debounce<T extends (...args: any[]) => void>(func: T, wait?: number, immediate?: boolean): () => void;
4
4
  export declare function isEqual(a: any, b: any): boolean;
5
5
  export declare function kebabCase(str: string): string;
@@ -36,10 +36,11 @@ export default class SlimSelect {
36
36
  setSelected(value: string | string[], runAfterChange?: boolean): void;
37
37
  addOption(option: OptionOptional): void;
38
38
  open(): void;
39
- close(): void;
39
+ close(eventType?: string | null): void;
40
40
  search(value: string): void;
41
41
  destroy(): void;
42
42
  private windowResize;
43
43
  private windowScroll;
44
44
  private documentClick;
45
+ private windowVisibilityChange;
45
46
  }
@@ -91,8 +91,9 @@ export default class Render {
91
91
  open(): void;
92
92
  close(): void;
93
93
  updateClassStyles(): void;
94
+ updateAriaAttributes(): void;
94
95
  mainDiv(): Main;
95
- mainFocus(trigger: boolean): void;
96
+ mainFocus(trigger: boolean, eventType: string | null): void;
96
97
  placeholder(): HTMLDivElement;
97
98
  renderValues(): void;
98
99
  private renderSingleValue;
@@ -6,6 +6,7 @@ export default class Settings {
6
6
  class: string[];
7
7
  isMultiple: boolean;
8
8
  isOpen: boolean;
9
+ isWindowFocused: boolean;
9
10
  triggerFocus: boolean;
10
11
  intervalMove: NodeJS.Timeout | null;
11
12
  disabled: boolean;
@@ -59,6 +59,7 @@ class Settings {
59
59
  this.class = [];
60
60
  this.isMultiple = false;
61
61
  this.isOpen = false;
62
+ this.isWindowFocused = true;
62
63
  this.triggerFocus = true;
63
64
  this.intervalMove = null;
64
65
  if (!settings) {
@@ -346,6 +347,7 @@ class Render {
346
347
  this.main = this.mainDiv();
347
348
  this.content = this.contentDiv();
348
349
  this.updateClassStyles();
350
+ this.updateAriaAttributes();
349
351
  this.settings.contentLocation.appendChild(this.content.main);
350
352
  }
351
353
  enable() {
@@ -359,6 +361,7 @@ class Render {
359
361
  open() {
360
362
  this.main.arrow.path.setAttribute('d', this.classes.arrowOpen);
361
363
  this.main.main.classList.add(this.settings.openPosition === 'up' ? this.classes.openAbove : this.classes.openBelow);
364
+ this.main.main.setAttribute('aria-expanded', 'true');
362
365
  this.moveContent();
363
366
  const selectedOptions = this.store.getSelectedOptions();
364
367
  if (selectedOptions.length) {
@@ -372,6 +375,7 @@ class Render {
372
375
  close() {
373
376
  this.main.main.classList.remove(this.classes.openAbove);
374
377
  this.main.main.classList.remove(this.classes.openBelow);
378
+ this.main.main.setAttribute('aria-expanded', 'false');
375
379
  this.content.main.classList.remove(this.classes.openAbove);
376
380
  this.content.main.classList.remove(this.classes.openBelow);
377
381
  this.main.arrow.path.setAttribute('d', this.classes.arrowClose);
@@ -398,15 +402,18 @@ class Render {
398
402
  this.content.main.classList.add('ss-' + this.settings.contentPosition);
399
403
  }
400
404
  }
405
+ updateAriaAttributes() {
406
+ this.main.main.role = 'combobox';
407
+ this.main.main.setAttribute('aria-haspopup', 'listbox');
408
+ this.main.main.setAttribute('aria-controls', this.content.main.id);
409
+ this.main.main.setAttribute('aria-expanded', 'false');
410
+ this.content.main.setAttribute('role', 'listbox');
411
+ }
401
412
  mainDiv() {
402
413
  const main = document.createElement('div');
403
414
  main.dataset.id = this.settings.id;
415
+ main.id = this.settings.id;
404
416
  main.tabIndex = 0;
405
- main.onfocus = () => {
406
- if (this.settings.triggerFocus) {
407
- this.callbacks.open();
408
- }
409
- };
410
417
  main.onkeydown = (e) => {
411
418
  switch (e.key) {
412
419
  case 'ArrowUp':
@@ -418,6 +425,8 @@ class Render {
418
425
  this.callbacks.close();
419
426
  return true;
420
427
  case 'Enter':
428
+ case ' ':
429
+ this.callbacks.open();
421
430
  const highlighted = this.content.list.querySelector('.' + this.classes.highlighted);
422
431
  if (highlighted) {
423
432
  highlighted.click();
@@ -494,11 +503,13 @@ class Render {
494
503
  },
495
504
  };
496
505
  }
497
- mainFocus(trigger) {
506
+ mainFocus(trigger, eventType) {
498
507
  if (!trigger) {
499
508
  this.settings.triggerFocus = false;
500
509
  }
501
- this.main.main.focus({ preventScroll: true });
510
+ if (eventType !== 'click') {
511
+ this.main.main.focus({ preventScroll: true });
512
+ }
502
513
  this.settings.triggerFocus = true;
503
514
  }
504
515
  placeholder() {
@@ -675,6 +686,7 @@ class Render {
675
686
  contentDiv() {
676
687
  const main = document.createElement('div');
677
688
  main.dataset.id = this.settings.id;
689
+ main.id = this.settings.id;
678
690
  const search = this.searchDiv();
679
691
  main.appendChild(search.main);
680
692
  const list = this.listDiv();
@@ -742,6 +754,7 @@ class Render {
742
754
  this.callbacks.close();
743
755
  return false;
744
756
  case 'Enter':
757
+ case ' ':
745
758
  if (this.callbacks.addable && e.ctrlKey) {
746
759
  addable.click();
747
760
  }
@@ -893,7 +906,6 @@ class Render {
893
906
  listDiv() {
894
907
  const options = document.createElement('div');
895
908
  options.classList.add(this.classes.list);
896
- options.setAttribute('role', 'listbox');
897
909
  return options;
898
910
  }
899
911
  renderError(error) {
@@ -1038,6 +1050,7 @@ class Render {
1038
1050
  }
1039
1051
  const optionEl = document.createElement('div');
1040
1052
  optionEl.dataset.id = option.id;
1053
+ optionEl.id = option.id;
1041
1054
  optionEl.classList.add(this.classes.option);
1042
1055
  optionEl.setAttribute('role', 'option');
1043
1056
  if (option.class) {
@@ -1071,9 +1084,12 @@ class Render {
1071
1084
  }
1072
1085
  if (option.selected) {
1073
1086
  optionEl.classList.add(this.classes.selected);
1087
+ optionEl.setAttribute('aria-selected', 'true');
1088
+ this.main.main.setAttribute('aria-activedescendant', optionEl.id);
1074
1089
  }
1075
1090
  else {
1076
1091
  optionEl.classList.remove(this.classes.selected);
1092
+ optionEl.setAttribute('aria-selected', 'false');
1077
1093
  }
1078
1094
  optionEl.addEventListener('click', (e) => {
1079
1095
  e.preventDefault();
@@ -1507,8 +1523,19 @@ class SlimSelect {
1507
1523
  return;
1508
1524
  }
1509
1525
  if (e.target && !hasClassInTree(e.target, this.settings.id)) {
1526
+ this.close(e.type);
1527
+ }
1528
+ };
1529
+ this.windowVisibilityChange = () => {
1530
+ if (document.hidden) {
1531
+ this.settings.isWindowFocused = false;
1510
1532
  this.close();
1511
1533
  }
1534
+ else {
1535
+ setTimeout(() => {
1536
+ this.settings.isWindowFocused = true;
1537
+ }, 20);
1538
+ }
1512
1539
  };
1513
1540
  this.selectEl = (typeof config.select === 'string' ? document.querySelector(config.select) : config.select);
1514
1541
  if (!this.selectEl) {
@@ -1581,6 +1608,14 @@ class SlimSelect {
1581
1608
  this.render = new Render(this.settings, this.store, callbacks);
1582
1609
  this.render.renderValues();
1583
1610
  this.render.renderOptions(this.store.getData());
1611
+ const selectAriaLabel = this.selectEl.getAttribute('aria-label');
1612
+ const selectAriaLabelledBy = this.selectEl.getAttribute('aria-labelledby');
1613
+ if (selectAriaLabel) {
1614
+ this.render.main.main.setAttribute('aria-label', selectAriaLabel);
1615
+ }
1616
+ else if (selectAriaLabelledBy) {
1617
+ this.render.main.main.setAttribute('aria-labelledby', selectAriaLabelledBy);
1618
+ }
1584
1619
  if (this.selectEl.parentNode) {
1585
1620
  this.selectEl.parentNode.insertBefore(this.render.main.main, this.selectEl.nextSibling);
1586
1621
  }
@@ -1589,6 +1624,7 @@ class SlimSelect {
1589
1624
  if (this.settings.openPosition === 'auto') {
1590
1625
  window.addEventListener('scroll', this.windowScroll, false);
1591
1626
  }
1627
+ document.addEventListener('visibilitychange', this.windowVisibilityChange);
1592
1628
  if (this.settings.disabled) {
1593
1629
  this.disable();
1594
1630
  }
@@ -1649,7 +1685,9 @@ class SlimSelect {
1649
1685
  }
1650
1686
  addOption(option) {
1651
1687
  const selected = this.store.getSelected();
1652
- this.store.addOption(option);
1688
+ if (!this.store.getDataOptions().some((o) => { var _a; return o.value === ((_a = option.value) !== null && _a !== void 0 ? _a : option.text); })) {
1689
+ this.store.addOption(option);
1690
+ }
1653
1691
  const data = this.store.getData();
1654
1692
  this.select.updateOptions(data);
1655
1693
  this.render.renderValues();
@@ -1682,7 +1720,7 @@ class SlimSelect {
1682
1720
  this.settings.intervalMove = setInterval(this.render.moveContent.bind(this.render), 500);
1683
1721
  }
1684
1722
  }
1685
- close() {
1723
+ close(eventType = null) {
1686
1724
  if (!this.settings.isOpen || this.settings.alwaysOpen) {
1687
1725
  return;
1688
1726
  }
@@ -1693,7 +1731,7 @@ class SlimSelect {
1693
1731
  if (this.render.content.search.input.value !== '') {
1694
1732
  this.search('');
1695
1733
  }
1696
- this.render.mainFocus(false);
1734
+ this.render.mainFocus(false, eventType);
1697
1735
  setTimeout(() => {
1698
1736
  if (this.events.afterClose) {
1699
1737
  this.events.afterClose();
@@ -1737,6 +1775,7 @@ class SlimSelect {
1737
1775
  if (this.settings.openPosition === 'auto') {
1738
1776
  window.removeEventListener('scroll', this.windowScroll, false);
1739
1777
  }
1778
+ document.removeEventListener('visibilitychange', this.windowVisibilityChange);
1740
1779
  this.store.setData([]);
1741
1780
  this.render.destroy();
1742
1781
  this.select.destroy();
@@ -60,6 +60,7 @@ var SlimSelectVue = (function (vue) {
60
60
  this.class = [];
61
61
  this.isMultiple = false;
62
62
  this.isOpen = false;
63
+ this.isWindowFocused = true;
63
64
  this.triggerFocus = true;
64
65
  this.intervalMove = null;
65
66
  if (!settings) {
@@ -347,6 +348,7 @@ var SlimSelectVue = (function (vue) {
347
348
  this.main = this.mainDiv();
348
349
  this.content = this.contentDiv();
349
350
  this.updateClassStyles();
351
+ this.updateAriaAttributes();
350
352
  this.settings.contentLocation.appendChild(this.content.main);
351
353
  }
352
354
  enable() {
@@ -360,6 +362,7 @@ var SlimSelectVue = (function (vue) {
360
362
  open() {
361
363
  this.main.arrow.path.setAttribute('d', this.classes.arrowOpen);
362
364
  this.main.main.classList.add(this.settings.openPosition === 'up' ? this.classes.openAbove : this.classes.openBelow);
365
+ this.main.main.setAttribute('aria-expanded', 'true');
363
366
  this.moveContent();
364
367
  const selectedOptions = this.store.getSelectedOptions();
365
368
  if (selectedOptions.length) {
@@ -373,6 +376,7 @@ var SlimSelectVue = (function (vue) {
373
376
  close() {
374
377
  this.main.main.classList.remove(this.classes.openAbove);
375
378
  this.main.main.classList.remove(this.classes.openBelow);
379
+ this.main.main.setAttribute('aria-expanded', 'false');
376
380
  this.content.main.classList.remove(this.classes.openAbove);
377
381
  this.content.main.classList.remove(this.classes.openBelow);
378
382
  this.main.arrow.path.setAttribute('d', this.classes.arrowClose);
@@ -399,15 +403,18 @@ var SlimSelectVue = (function (vue) {
399
403
  this.content.main.classList.add('ss-' + this.settings.contentPosition);
400
404
  }
401
405
  }
406
+ updateAriaAttributes() {
407
+ this.main.main.role = 'combobox';
408
+ this.main.main.setAttribute('aria-haspopup', 'listbox');
409
+ this.main.main.setAttribute('aria-controls', this.content.main.id);
410
+ this.main.main.setAttribute('aria-expanded', 'false');
411
+ this.content.main.setAttribute('role', 'listbox');
412
+ }
402
413
  mainDiv() {
403
414
  const main = document.createElement('div');
404
415
  main.dataset.id = this.settings.id;
416
+ main.id = this.settings.id;
405
417
  main.tabIndex = 0;
406
- main.onfocus = () => {
407
- if (this.settings.triggerFocus) {
408
- this.callbacks.open();
409
- }
410
- };
411
418
  main.onkeydown = (e) => {
412
419
  switch (e.key) {
413
420
  case 'ArrowUp':
@@ -419,6 +426,8 @@ var SlimSelectVue = (function (vue) {
419
426
  this.callbacks.close();
420
427
  return true;
421
428
  case 'Enter':
429
+ case ' ':
430
+ this.callbacks.open();
422
431
  const highlighted = this.content.list.querySelector('.' + this.classes.highlighted);
423
432
  if (highlighted) {
424
433
  highlighted.click();
@@ -495,11 +504,13 @@ var SlimSelectVue = (function (vue) {
495
504
  },
496
505
  };
497
506
  }
498
- mainFocus(trigger) {
507
+ mainFocus(trigger, eventType) {
499
508
  if (!trigger) {
500
509
  this.settings.triggerFocus = false;
501
510
  }
502
- this.main.main.focus({ preventScroll: true });
511
+ if (eventType !== 'click') {
512
+ this.main.main.focus({ preventScroll: true });
513
+ }
503
514
  this.settings.triggerFocus = true;
504
515
  }
505
516
  placeholder() {
@@ -676,6 +687,7 @@ var SlimSelectVue = (function (vue) {
676
687
  contentDiv() {
677
688
  const main = document.createElement('div');
678
689
  main.dataset.id = this.settings.id;
690
+ main.id = this.settings.id;
679
691
  const search = this.searchDiv();
680
692
  main.appendChild(search.main);
681
693
  const list = this.listDiv();
@@ -743,6 +755,7 @@ var SlimSelectVue = (function (vue) {
743
755
  this.callbacks.close();
744
756
  return false;
745
757
  case 'Enter':
758
+ case ' ':
746
759
  if (this.callbacks.addable && e.ctrlKey) {
747
760
  addable.click();
748
761
  }
@@ -894,7 +907,6 @@ var SlimSelectVue = (function (vue) {
894
907
  listDiv() {
895
908
  const options = document.createElement('div');
896
909
  options.classList.add(this.classes.list);
897
- options.setAttribute('role', 'listbox');
898
910
  return options;
899
911
  }
900
912
  renderError(error) {
@@ -1039,6 +1051,7 @@ var SlimSelectVue = (function (vue) {
1039
1051
  }
1040
1052
  const optionEl = document.createElement('div');
1041
1053
  optionEl.dataset.id = option.id;
1054
+ optionEl.id = option.id;
1042
1055
  optionEl.classList.add(this.classes.option);
1043
1056
  optionEl.setAttribute('role', 'option');
1044
1057
  if (option.class) {
@@ -1072,9 +1085,12 @@ var SlimSelectVue = (function (vue) {
1072
1085
  }
1073
1086
  if (option.selected) {
1074
1087
  optionEl.classList.add(this.classes.selected);
1088
+ optionEl.setAttribute('aria-selected', 'true');
1089
+ this.main.main.setAttribute('aria-activedescendant', optionEl.id);
1075
1090
  }
1076
1091
  else {
1077
1092
  optionEl.classList.remove(this.classes.selected);
1093
+ optionEl.setAttribute('aria-selected', 'false');
1078
1094
  }
1079
1095
  optionEl.addEventListener('click', (e) => {
1080
1096
  e.preventDefault();
@@ -1508,8 +1524,19 @@ var SlimSelectVue = (function (vue) {
1508
1524
  return;
1509
1525
  }
1510
1526
  if (e.target && !hasClassInTree(e.target, this.settings.id)) {
1527
+ this.close(e.type);
1528
+ }
1529
+ };
1530
+ this.windowVisibilityChange = () => {
1531
+ if (document.hidden) {
1532
+ this.settings.isWindowFocused = false;
1511
1533
  this.close();
1512
1534
  }
1535
+ else {
1536
+ setTimeout(() => {
1537
+ this.settings.isWindowFocused = true;
1538
+ }, 20);
1539
+ }
1513
1540
  };
1514
1541
  this.selectEl = (typeof config.select === 'string' ? document.querySelector(config.select) : config.select);
1515
1542
  if (!this.selectEl) {
@@ -1582,6 +1609,14 @@ var SlimSelectVue = (function (vue) {
1582
1609
  this.render = new Render(this.settings, this.store, callbacks);
1583
1610
  this.render.renderValues();
1584
1611
  this.render.renderOptions(this.store.getData());
1612
+ const selectAriaLabel = this.selectEl.getAttribute('aria-label');
1613
+ const selectAriaLabelledBy = this.selectEl.getAttribute('aria-labelledby');
1614
+ if (selectAriaLabel) {
1615
+ this.render.main.main.setAttribute('aria-label', selectAriaLabel);
1616
+ }
1617
+ else if (selectAriaLabelledBy) {
1618
+ this.render.main.main.setAttribute('aria-labelledby', selectAriaLabelledBy);
1619
+ }
1585
1620
  if (this.selectEl.parentNode) {
1586
1621
  this.selectEl.parentNode.insertBefore(this.render.main.main, this.selectEl.nextSibling);
1587
1622
  }
@@ -1590,6 +1625,7 @@ var SlimSelectVue = (function (vue) {
1590
1625
  if (this.settings.openPosition === 'auto') {
1591
1626
  window.addEventListener('scroll', this.windowScroll, false);
1592
1627
  }
1628
+ document.addEventListener('visibilitychange', this.windowVisibilityChange);
1593
1629
  if (this.settings.disabled) {
1594
1630
  this.disable();
1595
1631
  }
@@ -1650,7 +1686,9 @@ var SlimSelectVue = (function (vue) {
1650
1686
  }
1651
1687
  addOption(option) {
1652
1688
  const selected = this.store.getSelected();
1653
- this.store.addOption(option);
1689
+ if (!this.store.getDataOptions().some((o) => { var _a; return o.value === ((_a = option.value) !== null && _a !== void 0 ? _a : option.text); })) {
1690
+ this.store.addOption(option);
1691
+ }
1654
1692
  const data = this.store.getData();
1655
1693
  this.select.updateOptions(data);
1656
1694
  this.render.renderValues();
@@ -1683,7 +1721,7 @@ var SlimSelectVue = (function (vue) {
1683
1721
  this.settings.intervalMove = setInterval(this.render.moveContent.bind(this.render), 500);
1684
1722
  }
1685
1723
  }
1686
- close() {
1724
+ close(eventType = null) {
1687
1725
  if (!this.settings.isOpen || this.settings.alwaysOpen) {
1688
1726
  return;
1689
1727
  }
@@ -1694,7 +1732,7 @@ var SlimSelectVue = (function (vue) {
1694
1732
  if (this.render.content.search.input.value !== '') {
1695
1733
  this.search('');
1696
1734
  }
1697
- this.render.mainFocus(false);
1735
+ this.render.mainFocus(false, eventType);
1698
1736
  setTimeout(() => {
1699
1737
  if (this.events.afterClose) {
1700
1738
  this.events.afterClose();
@@ -1738,6 +1776,7 @@ var SlimSelectVue = (function (vue) {
1738
1776
  if (this.settings.openPosition === 'auto') {
1739
1777
  window.removeEventListener('scroll', this.windowScroll, false);
1740
1778
  }
1779
+ document.removeEventListener('visibilitychange', this.windowVisibilityChange);
1741
1780
  this.store.setData([]);
1742
1781
  this.render.destroy();
1743
1782
  this.select.destroy();