github.com/aminovpavel/nomad@v0.11.8/ui/app/components/multi-select-dropdown.js (about)

     1  import Component from '@ember/component';
     2  import { computed as overridable } from 'ember-overridable-computed';
     3  import { run } from '@ember/runloop';
     4  
     5  const TAB = 9;
     6  const ESC = 27;
     7  const SPACE = 32;
     8  const ARROW_UP = 38;
     9  const ARROW_DOWN = 40;
    10  
    11  export default Component.extend({
    12    classNames: ['dropdown'],
    13  
    14    options: overridable(() => []),
    15    selection: overridable(() => []),
    16  
    17    onSelect() {},
    18  
    19    isOpen: false,
    20    dropdown: null,
    21  
    22    capture(dropdown) {
    23      // It's not a good idea to grab a dropdown reference like this, but it's necessary
    24      // in order to invoke dropdown.actions.close in traverseList as well as
    25      // dropdown.actions.reposition when the label or selection length changes.
    26      this.set('dropdown', dropdown);
    27    },
    28  
    29    didReceiveAttrs() {
    30      const dropdown = this.dropdown;
    31      if (this.isOpen && dropdown) {
    32        run.scheduleOnce('afterRender', () => {
    33          dropdown.actions.reposition();
    34        });
    35      }
    36    },
    37  
    38    actions: {
    39      toggle({ key }) {
    40        const newSelection = this.selection.slice();
    41        if (newSelection.includes(key)) {
    42          newSelection.removeObject(key);
    43        } else {
    44          newSelection.addObject(key);
    45        }
    46        this.onSelect(newSelection);
    47      },
    48  
    49      openOnArrowDown(dropdown, e) {
    50        this.capture(dropdown);
    51  
    52        if (!this.isOpen && e.keyCode === ARROW_DOWN) {
    53          dropdown.actions.open(e);
    54          e.preventDefault();
    55        } else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) {
    56          const optionsId = this.element.querySelector('.dropdown-trigger').getAttribute('aria-owns');
    57          const firstElement = document.querySelector(`#${optionsId} .dropdown-option`);
    58  
    59          if (firstElement) {
    60            firstElement.focus();
    61            e.preventDefault();
    62          }
    63        }
    64      },
    65  
    66      traverseList(option, e) {
    67        if (e.keyCode === ESC) {
    68          // Close the dropdown
    69          const dropdown = this.dropdown;
    70          if (dropdown) {
    71            dropdown.actions.close(e);
    72            // Return focus to the trigger so tab works as expected
    73            const trigger = this.element.querySelector('.dropdown-trigger');
    74            if (trigger) trigger.focus();
    75            e.preventDefault();
    76            this.set('dropdown', null);
    77          }
    78        } else if (e.keyCode === ARROW_UP) {
    79          // previous item
    80          const prev = e.target.previousElementSibling;
    81          if (prev) {
    82            prev.focus();
    83            e.preventDefault();
    84          }
    85        } else if (e.keyCode === ARROW_DOWN) {
    86          // next item
    87          const next = e.target.nextElementSibling;
    88          if (next) {
    89            next.focus();
    90            e.preventDefault();
    91          }
    92        } else if (e.keyCode === SPACE) {
    93          this.send('toggle', option);
    94          e.preventDefault();
    95        }
    96      },
    97    },
    98  });