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.
@@ -61,6 +61,7 @@ class Settings {
61
61
  this.class = [];
62
62
  this.isMultiple = false;
63
63
  this.isOpen = false;
64
+ this.isWindowFocused = true;
64
65
  this.triggerFocus = true;
65
66
  this.intervalMove = null;
66
67
  if (!settings) {
@@ -348,6 +349,7 @@ class Render {
348
349
  this.main = this.mainDiv();
349
350
  this.content = this.contentDiv();
350
351
  this.updateClassStyles();
352
+ this.updateAriaAttributes();
351
353
  this.settings.contentLocation.appendChild(this.content.main);
352
354
  }
353
355
  enable() {
@@ -361,6 +363,7 @@ class Render {
361
363
  open() {
362
364
  this.main.arrow.path.setAttribute('d', this.classes.arrowOpen);
363
365
  this.main.main.classList.add(this.settings.openPosition === 'up' ? this.classes.openAbove : this.classes.openBelow);
366
+ this.main.main.setAttribute('aria-expanded', 'true');
364
367
  this.moveContent();
365
368
  const selectedOptions = this.store.getSelectedOptions();
366
369
  if (selectedOptions.length) {
@@ -374,6 +377,7 @@ class Render {
374
377
  close() {
375
378
  this.main.main.classList.remove(this.classes.openAbove);
376
379
  this.main.main.classList.remove(this.classes.openBelow);
380
+ this.main.main.setAttribute('aria-expanded', 'false');
377
381
  this.content.main.classList.remove(this.classes.openAbove);
378
382
  this.content.main.classList.remove(this.classes.openBelow);
379
383
  this.main.arrow.path.setAttribute('d', this.classes.arrowClose);
@@ -400,15 +404,18 @@ class Render {
400
404
  this.content.main.classList.add('ss-' + this.settings.contentPosition);
401
405
  }
402
406
  }
407
+ updateAriaAttributes() {
408
+ this.main.main.role = 'combobox';
409
+ this.main.main.setAttribute('aria-haspopup', 'listbox');
410
+ this.main.main.setAttribute('aria-controls', this.content.main.id);
411
+ this.main.main.setAttribute('aria-expanded', 'false');
412
+ this.content.main.setAttribute('role', 'listbox');
413
+ }
403
414
  mainDiv() {
404
415
  const main = document.createElement('div');
405
416
  main.dataset.id = this.settings.id;
417
+ main.id = this.settings.id;
406
418
  main.tabIndex = 0;
407
- main.onfocus = () => {
408
- if (this.settings.triggerFocus) {
409
- this.callbacks.open();
410
- }
411
- };
412
419
  main.onkeydown = (e) => {
413
420
  switch (e.key) {
414
421
  case 'ArrowUp':
@@ -420,6 +427,8 @@ class Render {
420
427
  this.callbacks.close();
421
428
  return true;
422
429
  case 'Enter':
430
+ case ' ':
431
+ this.callbacks.open();
423
432
  const highlighted = this.content.list.querySelector('.' + this.classes.highlighted);
424
433
  if (highlighted) {
425
434
  highlighted.click();
@@ -496,11 +505,13 @@ class Render {
496
505
  },
497
506
  };
498
507
  }
499
- mainFocus(trigger) {
508
+ mainFocus(trigger, eventType) {
500
509
  if (!trigger) {
501
510
  this.settings.triggerFocus = false;
502
511
  }
503
- this.main.main.focus({ preventScroll: true });
512
+ if (eventType !== 'click') {
513
+ this.main.main.focus({ preventScroll: true });
514
+ }
504
515
  this.settings.triggerFocus = true;
505
516
  }
506
517
  placeholder() {
@@ -677,6 +688,7 @@ class Render {
677
688
  contentDiv() {
678
689
  const main = document.createElement('div');
679
690
  main.dataset.id = this.settings.id;
691
+ main.id = this.settings.id;
680
692
  const search = this.searchDiv();
681
693
  main.appendChild(search.main);
682
694
  const list = this.listDiv();
@@ -744,6 +756,7 @@ class Render {
744
756
  this.callbacks.close();
745
757
  return false;
746
758
  case 'Enter':
759
+ case ' ':
747
760
  if (this.callbacks.addable && e.ctrlKey) {
748
761
  addable.click();
749
762
  }
@@ -895,7 +908,6 @@ class Render {
895
908
  listDiv() {
896
909
  const options = document.createElement('div');
897
910
  options.classList.add(this.classes.list);
898
- options.setAttribute('role', 'listbox');
899
911
  return options;
900
912
  }
901
913
  renderError(error) {
@@ -1040,6 +1052,7 @@ class Render {
1040
1052
  }
1041
1053
  const optionEl = document.createElement('div');
1042
1054
  optionEl.dataset.id = option.id;
1055
+ optionEl.id = option.id;
1043
1056
  optionEl.classList.add(this.classes.option);
1044
1057
  optionEl.setAttribute('role', 'option');
1045
1058
  if (option.class) {
@@ -1073,9 +1086,12 @@ class Render {
1073
1086
  }
1074
1087
  if (option.selected) {
1075
1088
  optionEl.classList.add(this.classes.selected);
1089
+ optionEl.setAttribute('aria-selected', 'true');
1090
+ this.main.main.setAttribute('aria-activedescendant', optionEl.id);
1076
1091
  }
1077
1092
  else {
1078
1093
  optionEl.classList.remove(this.classes.selected);
1094
+ optionEl.setAttribute('aria-selected', 'false');
1079
1095
  }
1080
1096
  optionEl.addEventListener('click', (e) => {
1081
1097
  e.preventDefault();
@@ -1509,8 +1525,19 @@ class SlimSelect {
1509
1525
  return;
1510
1526
  }
1511
1527
  if (e.target && !hasClassInTree(e.target, this.settings.id)) {
1528
+ this.close(e.type);
1529
+ }
1530
+ };
1531
+ this.windowVisibilityChange = () => {
1532
+ if (document.hidden) {
1533
+ this.settings.isWindowFocused = false;
1512
1534
  this.close();
1513
1535
  }
1536
+ else {
1537
+ setTimeout(() => {
1538
+ this.settings.isWindowFocused = true;
1539
+ }, 20);
1540
+ }
1514
1541
  };
1515
1542
  this.selectEl = (typeof config.select === 'string' ? document.querySelector(config.select) : config.select);
1516
1543
  if (!this.selectEl) {
@@ -1583,6 +1610,14 @@ class SlimSelect {
1583
1610
  this.render = new Render(this.settings, this.store, callbacks);
1584
1611
  this.render.renderValues();
1585
1612
  this.render.renderOptions(this.store.getData());
1613
+ const selectAriaLabel = this.selectEl.getAttribute('aria-label');
1614
+ const selectAriaLabelledBy = this.selectEl.getAttribute('aria-labelledby');
1615
+ if (selectAriaLabel) {
1616
+ this.render.main.main.setAttribute('aria-label', selectAriaLabel);
1617
+ }
1618
+ else if (selectAriaLabelledBy) {
1619
+ this.render.main.main.setAttribute('aria-labelledby', selectAriaLabelledBy);
1620
+ }
1586
1621
  if (this.selectEl.parentNode) {
1587
1622
  this.selectEl.parentNode.insertBefore(this.render.main.main, this.selectEl.nextSibling);
1588
1623
  }
@@ -1591,6 +1626,7 @@ class SlimSelect {
1591
1626
  if (this.settings.openPosition === 'auto') {
1592
1627
  window.addEventListener('scroll', this.windowScroll, false);
1593
1628
  }
1629
+ document.addEventListener('visibilitychange', this.windowVisibilityChange);
1594
1630
  if (this.settings.disabled) {
1595
1631
  this.disable();
1596
1632
  }
@@ -1651,7 +1687,9 @@ class SlimSelect {
1651
1687
  }
1652
1688
  addOption(option) {
1653
1689
  const selected = this.store.getSelected();
1654
- this.store.addOption(option);
1690
+ if (!this.store.getDataOptions().some((o) => { var _a; return o.value === ((_a = option.value) !== null && _a !== void 0 ? _a : option.text); })) {
1691
+ this.store.addOption(option);
1692
+ }
1655
1693
  const data = this.store.getData();
1656
1694
  this.select.updateOptions(data);
1657
1695
  this.render.renderValues();
@@ -1684,7 +1722,7 @@ class SlimSelect {
1684
1722
  this.settings.intervalMove = setInterval(this.render.moveContent.bind(this.render), 500);
1685
1723
  }
1686
1724
  }
1687
- close() {
1725
+ close(eventType = null) {
1688
1726
  if (!this.settings.isOpen || this.settings.alwaysOpen) {
1689
1727
  return;
1690
1728
  }
@@ -1695,7 +1733,7 @@ class SlimSelect {
1695
1733
  if (this.render.content.search.input.value !== '') {
1696
1734
  this.search('');
1697
1735
  }
1698
- this.render.mainFocus(false);
1736
+ this.render.mainFocus(false, eventType);
1699
1737
  setTimeout(() => {
1700
1738
  if (this.events.afterClose) {
1701
1739
  this.events.afterClose();
@@ -1739,6 +1777,7 @@ class SlimSelect {
1739
1777
  if (this.settings.openPosition === 'auto') {
1740
1778
  window.removeEventListener('scroll', this.windowScroll, false);
1741
1779
  }
1780
+ document.removeEventListener('visibilitychange', this.windowVisibilityChange);
1742
1781
  this.store.setData([]);
1743
1782
  this.render.destroy();
1744
1783
  this.select.destroy();
@@ -35,6 +35,7 @@ declare const _default: import("vue").DefineComponent<{
35
35
  class: string[];
36
36
  isMultiple: boolean;
37
37
  isOpen: boolean;
38
+ isWindowFocused: boolean;
38
39
  triggerFocus: boolean;
39
40
  intervalMove: {
40
41
  hasRef: () => boolean;
@@ -112,6 +113,7 @@ declare const _default: import("vue").DefineComponent<{
112
113
  class: string[];
113
114
  isMultiple: boolean;
114
115
  isOpen: boolean;
116
+ isWindowFocused: boolean;
115
117
  triggerFocus: boolean;
116
118
  intervalMove: {
117
119
  hasRef: () => boolean;
@@ -241,8 +243,9 @@ declare const _default: import("vue").DefineComponent<{
241
243
  open: () => void;
242
244
  close: () => void;
243
245
  updateClassStyles: () => void;
246
+ updateAriaAttributes: () => void;
244
247
  mainDiv: () => import("../slim-select/render").Main;
245
- mainFocus: (trigger: boolean) => void;
248
+ mainFocus: (trigger: boolean, eventType: string | null) => void;
246
249
  placeholder: () => HTMLDivElement;
247
250
  renderValues: () => void;
248
251
  multipleValue: (option: Option) => HTMLDivElement;
@@ -283,7 +286,7 @@ declare const _default: import("vue").DefineComponent<{
283
286
  setSelected: (value: string | string[], runAfterChange?: boolean) => void;
284
287
  addOption: (option: import("../slim-select/store").OptionOptional) => void;
285
288
  open: () => void;
286
- close: () => void;
289
+ close: (eventType?: string | null) => void;
287
290
  search: (value: string) => void;
288
291
  destroy: () => void;
289
292
  } | null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@slim-select/vue",
3
3
  "private": false,
4
- "version": "1.3.2",
4
+ "version": "1.4.0",
5
5
  "description": "Vue integration",
6
6
  "repository": {
7
7
  "type": "git",
@@ -16,6 +16,7 @@
16
16
  "bugs": {
17
17
  "url": "https://github.com/brianvoe/slim-select/issues"
18
18
  },
19
+ "type": "module",
19
20
  "main": "dist/slimselectvue.ssr.js",
20
21
  "browser": "dist/slimselect.global.js",
21
22
  "module": "dist/slimselectvue.es.js",