summaryrefslogtreecommitdiffstats
path: root/client/lib/textComplete.js
blob: e97d38534aeda73e91049f38e191ae004281e7b3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// We “inherit” the jquery-textcomplete plugin to integrate with our
// EscapeActions system. You should always use `escapeableTextComplete` instead
// of the vanilla `textcomplete`.
let dropdownMenuIsOpened = false;

$.fn.escapeableTextComplete = function(strategies, options, ...otherArgs) {
  // When the autocomplete menu is shown we want both a press of both `Tab`
  // or `Enter` to validation the auto-completion. We also need to stop the
  // event propagation to prevent EscapeActions side effect, for instance the
  // minicard submission (on `Enter`) or going on the next column (on `Tab`).
  options = {
    onKeydown(evt, commands) {
      if (evt.keyCode === 9 || evt.keyCode === 13) {
        evt.stopPropagation();
        return commands.KEY_ENTER;
      }
      return null;
    },
    ...options,
  };

  // Proxy to the vanilla jQuery component
  this.textcomplete(strategies, options, ...otherArgs);

  // Since commit d474017 jquery-textComplete automatically closes a potential
  // opened dropdown menu when the user press Escape. This behavior conflicts
  // with our EscapeActions system, but it's too complicated and hacky to
  // monkey-pach textComplete to disable it -- I tried. Instead we listen to
  // 'open' and 'hide' events, and create a ghost escapeAction when the dropdown
  // is opened (and rely on textComplete to execute the actual action).
  this.on({
    'textComplete:show'() {
      dropdownMenuIsOpened = true;
    },
    'textComplete:hide'() {
      Tracker.afterFlush(() => {
        // XXX Hack. We unfortunately need to set a setTimeout here to make the
        // `noClickEscapeOn` work bellow, otherwise clicking on a autocomplete
        // item will close both the autocomplete menu (as expected) but also the
        // next item in the stack (for example the minicard editor) which we
        // don't want.
        setTimeout(() => {
          dropdownMenuIsOpened = false;
        }, 100);
      });
    },
  });
  return this;
};

EscapeActions.register(
  'textcomplete',
  () => {},
  () => dropdownMenuIsOpened,
  {
    noClickEscapeOn: '.textcomplete-dropdown',
  },
);