| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 | // Pressing `Escape` should close the last opened “element” and only the last// one. Components can register themselves using a label a condition, and an// action. This is used by Popup or inlinedForm for instance. When we press// escape we execute the action which have a valid condition and his the highest// in the label hierarchy.EscapeActions = {  _nextclickPrevented: false,  _actions: [],  // Executed in order  hierarchy: [    'textcomplete',    'popup-back',    'popup-close',    'modalWindow',    'inlinedForm',    'detailsPane',    'multiselection',    'sidebarView',  ],  register(label, action, condition = () => true, options = {}) {    const priority = this.hierarchy.indexOf(label);    if (priority === -1) {      throw Error('You must define the label in the EscapeActions hierarchy');    }    let enabledOnClick = options.enabledOnClick;    if (_.isUndefined(enabledOnClick)) {      enabledOnClick = true;    }    const noClickEscapeOn = options.noClickEscapeOn;    this._actions = _.sortBy([...this._actions, {      priority,      condition,      action,      noClickEscapeOn,      enabledOnClick,    }], (action) => action.priority);  },  executeLowest() {    return this._execute({      multipleAction: false,    });  },  executeAll() {    return this._execute({      multipleActions: true,    });  },  executeUpTo(maxLabel) {    return this._execute({      maxLabel,      multipleActions: true,    });  },  clickExecute(target, maxLabel) {    if (this._nextclickPrevented) {      this._nextclickPrevented = false;      return false;    } else {      return this._execute({        maxLabel,        multipleActions: false,        isClick: true,        clickTarget: target,      });    }  },  preventNextClick() {    this._nextclickPrevented = true;  },  _stopClick(action, clickTarget) {    if (!_.isString(action.noClickEscapeOn))      return false;    else      return $(clickTarget).closest(action.noClickEscapeOn).length > 0;  },  _execute(options) {    const maxLabel = options.maxLabel;    const multipleActions = options.multipleActions;    const isClick = Boolean(options.isClick);    const clickTarget = options.clickTarget;    let executedAtLeastOne = false;    let maxPriority;    if (!maxLabel)      maxPriority = Infinity;    else      maxPriority = this.hierarchy.indexOf(maxLabel);    for (const currentAction of this._actions) {      if (currentAction.priority > maxPriority)        return executedAtLeastOne;      if (isClick && this._stopClick(currentAction, clickTarget))        return executedAtLeastOne;      const isEnabled = currentAction.enabledOnClick || !isClick;      if (isEnabled && currentAction.condition()) {        currentAction.action();        executedAtLeastOne = true;        if (!multipleActions)          return executedAtLeastOne;      }    }    return executedAtLeastOne;  },};// Pressing escape to execute one escape action. We use `bindGloabal` vecause// the shortcut sould work on textarea and inputs as well.Mousetrap.bindGlobal('esc', () => {  EscapeActions.executeLowest();});// On a left click on the document, we try to exectute one escape action (eg,// close the popup). We don't execute any action if the user has clicked on a// link or a button.$(document).on('click', (evt) => {  if (evt.button === 0 &&    $(evt.target).closest('a,button,.is-editable').length === 0) {    EscapeActions.clickExecute(evt.target, 'multiselection');  }});
 |