slim-select 2.3.3 → 2.4.1

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,8 +61,7 @@ class Settings {
61
61
  this.class = [];
62
62
  this.isMultiple = false;
63
63
  this.isOpen = false;
64
- this.isWindowFocused = true;
65
- this.triggerFocus = true;
64
+ this.isFullOpen = false;
66
65
  this.intervalMove = null;
67
66
  if (!settings) {
68
67
  settings = {};
@@ -349,6 +348,7 @@ class Render {
349
348
  this.main = this.mainDiv();
350
349
  this.content = this.contentDiv();
351
350
  this.updateClassStyles();
351
+ this.updateAriaAttributes();
352
352
  this.settings.contentLocation.appendChild(this.content.main);
353
353
  }
354
354
  enable() {
@@ -362,6 +362,7 @@ class Render {
362
362
  open() {
363
363
  this.main.arrow.path.setAttribute('d', this.classes.arrowOpen);
364
364
  this.main.main.classList.add(this.settings.openPosition === 'up' ? this.classes.openAbove : this.classes.openBelow);
365
+ this.main.main.setAttribute('aria-expanded', 'true');
365
366
  this.moveContent();
366
367
  const selectedOptions = this.store.getSelectedOptions();
367
368
  if (selectedOptions.length) {
@@ -375,6 +376,7 @@ class Render {
375
376
  close() {
376
377
  this.main.main.classList.remove(this.classes.openAbove);
377
378
  this.main.main.classList.remove(this.classes.openBelow);
379
+ this.main.main.setAttribute('aria-expanded', 'false');
378
380
  this.content.main.classList.remove(this.classes.openAbove);
379
381
  this.content.main.classList.remove(this.classes.openBelow);
380
382
  this.main.arrow.path.setAttribute('d', this.classes.arrowClose);
@@ -401,15 +403,18 @@ class Render {
401
403
  this.content.main.classList.add('ss-' + this.settings.contentPosition);
402
404
  }
403
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
+ }
404
413
  mainDiv() {
405
414
  const main = document.createElement('div');
406
415
  main.dataset.id = this.settings.id;
416
+ main.id = this.settings.id;
407
417
  main.tabIndex = 0;
408
- main.onfocus = () => {
409
- if (this.settings.triggerFocus && this.settings.isWindowFocused) {
410
- this.callbacks.open();
411
- }
412
- };
413
418
  main.onkeydown = (e) => {
414
419
  switch (e.key) {
415
420
  case 'ArrowUp':
@@ -421,6 +426,8 @@ class Render {
421
426
  this.callbacks.close();
422
427
  return true;
423
428
  case 'Enter':
429
+ case ' ':
430
+ this.callbacks.open();
424
431
  const highlighted = this.content.list.querySelector('.' + this.classes.highlighted);
425
432
  if (highlighted) {
426
433
  highlighted.click();
@@ -497,12 +504,10 @@ class Render {
497
504
  },
498
505
  };
499
506
  }
500
- mainFocus(trigger) {
501
- if (!trigger) {
502
- this.settings.triggerFocus = false;
507
+ mainFocus(eventType) {
508
+ if (eventType !== 'click') {
509
+ this.main.main.focus({ preventScroll: true });
503
510
  }
504
- this.main.main.focus({ preventScroll: true });
505
- this.settings.triggerFocus = true;
506
511
  }
507
512
  placeholder() {
508
513
  const placeholderOption = this.store.filter((o) => o.placeholder, false);
@@ -678,6 +683,7 @@ class Render {
678
683
  contentDiv() {
679
684
  const main = document.createElement('div');
680
685
  main.dataset.id = this.settings.id;
686
+ main.id = this.settings.id;
681
687
  const search = this.searchDiv();
682
688
  main.appendChild(search.main);
683
689
  const list = this.listDiv();
@@ -735,7 +741,6 @@ class Render {
735
741
  switch (e.key) {
736
742
  case 'ArrowUp':
737
743
  case 'ArrowDown':
738
- this.callbacks.open();
739
744
  e.key === 'ArrowDown' ? this.highlight('down') : this.highlight('up');
740
745
  return false;
741
746
  case 'Tab':
@@ -745,6 +750,7 @@ class Render {
745
750
  this.callbacks.close();
746
751
  return false;
747
752
  case 'Enter':
753
+ case ' ':
748
754
  if (this.callbacks.addable && e.ctrlKey) {
749
755
  addable.click();
750
756
  }
@@ -757,12 +763,6 @@ class Render {
757
763
  return false;
758
764
  }
759
765
  };
760
- input.onfocus = () => {
761
- if (this.settings.isOpen) {
762
- return;
763
- }
764
- this.callbacks.open();
765
- };
766
766
  main.appendChild(input);
767
767
  if (this.callbacks.addable) {
768
768
  addable.classList.add(this.classes.addable);
@@ -835,12 +835,8 @@ class Render {
835
835
  }
836
836
  return searchReturn;
837
837
  }
838
- searchFocus(trigger) {
839
- if (!trigger) {
840
- this.settings.triggerFocus = false;
841
- }
838
+ searchFocus() {
842
839
  this.content.search.input.focus();
843
- this.settings.triggerFocus = true;
844
840
  }
845
841
  getOptions(notPlaceholder = false, notDisabled = false, notHidden = false) {
846
842
  let query = '.' + this.classes.option;
@@ -896,7 +892,6 @@ class Render {
896
892
  listDiv() {
897
893
  const options = document.createElement('div');
898
894
  options.classList.add(this.classes.list);
899
- options.setAttribute('role', 'listbox');
900
895
  return options;
901
896
  }
902
897
  renderError(error) {
@@ -1041,6 +1036,7 @@ class Render {
1041
1036
  }
1042
1037
  const optionEl = document.createElement('div');
1043
1038
  optionEl.dataset.id = option.id;
1039
+ optionEl.id = option.id;
1044
1040
  optionEl.classList.add(this.classes.option);
1045
1041
  optionEl.setAttribute('role', 'option');
1046
1042
  if (option.class) {
@@ -1074,9 +1070,12 @@ class Render {
1074
1070
  }
1075
1071
  if (option.selected) {
1076
1072
  optionEl.classList.add(this.classes.selected);
1073
+ optionEl.setAttribute('aria-selected', 'true');
1074
+ this.main.main.setAttribute('aria-activedescendant', optionEl.id);
1077
1075
  }
1078
1076
  else {
1079
1077
  optionEl.classList.remove(this.classes.selected);
1078
+ optionEl.setAttribute('aria-selected', 'false');
1080
1079
  }
1081
1080
  optionEl.addEventListener('click', (e) => {
1082
1081
  e.preventDefault();
@@ -1494,13 +1493,13 @@ class SlimSelect {
1494
1493
  afterClose: undefined,
1495
1494
  };
1496
1495
  this.windowResize = debounce(() => {
1497
- if (!this.settings.isOpen) {
1496
+ if (!this.settings.isOpen && !this.settings.isFullOpen) {
1498
1497
  return;
1499
1498
  }
1500
1499
  this.render.moveContent();
1501
1500
  });
1502
1501
  this.windowScroll = debounce(() => {
1503
- if (!this.settings.isOpen) {
1502
+ if (!this.settings.isOpen && !this.settings.isFullOpen) {
1504
1503
  return;
1505
1504
  }
1506
1505
  this.render.moveContent();
@@ -1510,19 +1509,13 @@ class SlimSelect {
1510
1509
  return;
1511
1510
  }
1512
1511
  if (e.target && !hasClassInTree(e.target, this.settings.id)) {
1513
- this.close();
1512
+ this.close(e.type);
1514
1513
  }
1515
1514
  };
1516
1515
  this.windowVisibilityChange = () => {
1517
1516
  if (document.hidden) {
1518
- this.settings.isWindowFocused = false;
1519
1517
  this.close();
1520
1518
  }
1521
- else {
1522
- setTimeout(() => {
1523
- this.settings.isWindowFocused = true;
1524
- }, 20);
1525
- }
1526
1519
  };
1527
1520
  this.selectEl = (typeof config.select === 'string' ? document.querySelector(config.select) : config.select);
1528
1521
  if (!this.selectEl) {
@@ -1595,6 +1588,14 @@ class SlimSelect {
1595
1588
  this.render = new Render(this.settings, this.store, callbacks);
1596
1589
  this.render.renderValues();
1597
1590
  this.render.renderOptions(this.store.getData());
1591
+ const selectAriaLabel = this.selectEl.getAttribute('aria-label');
1592
+ const selectAriaLabelledBy = this.selectEl.getAttribute('aria-labelledby');
1593
+ if (selectAriaLabel) {
1594
+ this.render.main.main.setAttribute('aria-label', selectAriaLabel);
1595
+ }
1596
+ else if (selectAriaLabelledBy) {
1597
+ this.render.main.main.setAttribute('aria-labelledby', selectAriaLabelledBy);
1598
+ }
1598
1599
  if (this.selectEl.parentNode) {
1599
1600
  this.selectEl.parentNode.insertBefore(this.render.main.main, this.selectEl.nextSibling);
1600
1601
  }
@@ -1684,13 +1685,16 @@ class SlimSelect {
1684
1685
  }
1685
1686
  this.render.open();
1686
1687
  if (this.settings.showSearch) {
1687
- this.render.searchFocus(false);
1688
+ this.render.searchFocus();
1688
1689
  }
1690
+ this.settings.isOpen = true;
1689
1691
  setTimeout(() => {
1690
1692
  if (this.events.afterOpen) {
1691
1693
  this.events.afterOpen();
1692
1694
  }
1693
- this.settings.isOpen = true;
1695
+ if (this.settings.isOpen) {
1696
+ this.settings.isFullOpen = true;
1697
+ }
1694
1698
  }, this.settings.timeoutDelay);
1695
1699
  if (this.settings.contentPosition === 'absolute') {
1696
1700
  if (this.settings.intervalMove) {
@@ -1699,7 +1703,7 @@ class SlimSelect {
1699
1703
  this.settings.intervalMove = setInterval(this.render.moveContent.bind(this.render), 500);
1700
1704
  }
1701
1705
  }
1702
- close() {
1706
+ close(eventType = null) {
1703
1707
  if (!this.settings.isOpen || this.settings.alwaysOpen) {
1704
1708
  return;
1705
1709
  }
@@ -1710,12 +1714,13 @@ class SlimSelect {
1710
1714
  if (this.render.content.search.input.value !== '') {
1711
1715
  this.search('');
1712
1716
  }
1713
- this.render.mainFocus(false);
1717
+ this.render.mainFocus(eventType);
1718
+ this.settings.isOpen = false;
1719
+ this.settings.isFullOpen = false;
1714
1720
  setTimeout(() => {
1715
1721
  if (this.events.afterClose) {
1716
1722
  this.events.afterClose();
1717
1723
  }
1718
- this.settings.isOpen = false;
1719
1724
  }, this.settings.timeoutDelay);
1720
1725
  if (this.settings.intervalMove) {
1721
1726
  clearInterval(this.settings.intervalMove);
@@ -35,8 +35,7 @@ declare const _default: import("vue").DefineComponent<{
35
35
  class: string[];
36
36
  isMultiple: boolean;
37
37
  isOpen: boolean;
38
- isWindowFocused: boolean;
39
- triggerFocus: boolean;
38
+ isFullOpen: boolean;
40
39
  intervalMove: {
41
40
  hasRef: () => boolean;
42
41
  refresh: () => NodeJS.Timeout;
@@ -113,8 +112,7 @@ declare const _default: import("vue").DefineComponent<{
113
112
  class: string[];
114
113
  isMultiple: boolean;
115
114
  isOpen: boolean;
116
- isWindowFocused: boolean;
117
- triggerFocus: boolean;
115
+ isFullOpen: boolean;
118
116
  intervalMove: {
119
117
  hasRef: () => boolean;
120
118
  refresh: () => NodeJS.Timeout;
@@ -243,15 +241,16 @@ declare const _default: import("vue").DefineComponent<{
243
241
  open: () => void;
244
242
  close: () => void;
245
243
  updateClassStyles: () => void;
244
+ updateAriaAttributes: () => void;
246
245
  mainDiv: () => import("../slim-select/render").Main;
247
- mainFocus: (trigger: boolean) => void;
246
+ mainFocus: (eventType: string | null) => void;
248
247
  placeholder: () => HTMLDivElement;
249
248
  renderValues: () => void;
250
249
  multipleValue: (option: Option) => HTMLDivElement;
251
250
  contentDiv: () => import("../slim-select/render").Content;
252
251
  moveContent: () => void;
253
252
  searchDiv: () => import("../slim-select/render").Search;
254
- searchFocus: (trigger: boolean) => void;
253
+ searchFocus: () => void;
255
254
  getOptions: (notPlaceholder?: boolean, notDisabled?: boolean, notHidden?: boolean) => HTMLDivElement[];
256
255
  highlight: (dir: "up" | "down") => void;
257
256
  listDiv: () => HTMLDivElement;
@@ -285,7 +284,7 @@ declare const _default: import("vue").DefineComponent<{
285
284
  setSelected: (value: string | string[], runAfterChange?: boolean) => void;
286
285
  addOption: (option: import("../slim-select/store").OptionOptional) => void;
287
286
  open: () => void;
288
- close: () => void;
287
+ close: (eventType?: string | null) => void;
289
288
  search: (value: string) => void;
290
289
  destroy: () => void;
291
290
  } | null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@slim-select/vue",
3
3
  "private": false,
4
- "version": "1.3.3",
4
+ "version": "1.4.1",
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",