1import { each, reduce } from 'lodash';
2import $ from 'jquery';
3import coreModule from './core_module';
4
5/** @ngInject */
6export function dropdownTypeahead($compile: any) {
7  const inputTemplate =
8    '<input type="text"' +
9    ' class="gf-form-input input-medium tight-form-input"' +
10    ' spellcheck="false" style="display:none"></input>';
11
12  const buttonTemplate =
13    '<a class="gf-form-label tight-form-func dropdown-toggle"' +
14    ' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
15    ' ><i class="fa fa-plus"></i></a>';
16
17  return {
18    scope: {
19      menuItems: '=dropdownTypeahead',
20      dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
21      model: '=ngModel',
22    },
23    link: ($scope: any, elem: any, attrs: any) => {
24      const $input = $(inputTemplate);
25      const $button = $(buttonTemplate);
26      $input.appendTo(elem);
27      $button.appendTo(elem);
28
29      if (attrs.linkText) {
30        $button.html(attrs.linkText);
31      }
32
33      if (attrs.ngModel) {
34        $scope.$watch('model', (newValue: any) => {
35          each($scope.menuItems, (item) => {
36            each(item.submenu, (subItem) => {
37              if (subItem.value === newValue) {
38                $button.html(subItem.text);
39              }
40            });
41          });
42        });
43      }
44
45      const typeaheadValues = reduce(
46        $scope.menuItems,
47        (memo: any[], value, index) => {
48          if (!value.submenu) {
49            value.click = 'menuItemSelected(' + index + ')';
50            memo.push(value.text);
51          } else {
52            each(value.submenu, (item, subIndex) => {
53              item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';
54              memo.push(value.text + ' ' + item.text);
55            });
56          }
57          return memo;
58        },
59        []
60      );
61
62      const closeDropdownMenu = () => {
63        $input.hide();
64        $input.val('');
65        $button.show();
66        $button.focus();
67        elem.removeClass('open');
68      };
69
70      $scope.menuItemSelected = (index: number, subIndex: number) => {
71        const menuItem = $scope.menuItems[index];
72        const payload: any = { $item: menuItem };
73        if (menuItem.submenu && subIndex !== void 0) {
74          payload.$subItem = menuItem.submenu[subIndex];
75        }
76        $scope.dropdownTypeaheadOnSelect(payload);
77        closeDropdownMenu();
78      };
79
80      $input.attr('data-provide', 'typeahead');
81      $input.typeahead({
82        source: typeaheadValues,
83        minLength: 1,
84        items: 10,
85        updater: (value: string) => {
86          const result: any = {};
87          each($scope.menuItems, (menuItem) => {
88            each(menuItem.submenu, (submenuItem) => {
89              if (value === menuItem.text + ' ' + submenuItem.text) {
90                result.$subItem = submenuItem;
91                result.$item = menuItem;
92              }
93            });
94          });
95
96          if (result.$item) {
97            $scope.$apply(() => {
98              $scope.dropdownTypeaheadOnSelect(result);
99            });
100          }
101
102          $input.trigger('blur');
103          return '';
104        },
105      });
106
107      $button.click(() => {
108        $button.hide();
109        $input.show();
110        $input.focus();
111      });
112
113      $input.keyup(() => {
114        elem.toggleClass('open', $input.val() === '');
115      });
116
117      elem.mousedown((evt: Event) => {
118        evt.preventDefault();
119      });
120
121      $input.blur(() => {
122        $input.hide();
123        $input.val('');
124        $button.show();
125        $button.focus();
126        // clicking the function dropdown menu won't
127        // work if you remove class at once
128        setTimeout(() => {
129          elem.removeClass('open');
130        }, 200);
131      });
132
133      $compile(elem.contents())($scope);
134    },
135  };
136}
137
138/** @ngInject */
139export function dropdownTypeahead2($compile: any) {
140  const inputTemplate =
141    '<input type="text"' + ' class="gf-form-input"' + ' spellcheck="false" style="display:none"></input>';
142
143  const buttonTemplate =
144    '<a class="{{buttonTemplateClass}} dropdown-toggle"' +
145    ' tabindex="1" gf-dropdown="menuItems" data-toggle="dropdown"' +
146    ' ><i class="fa fa-plus"></i></a>';
147
148  return {
149    scope: {
150      menuItems: '=dropdownTypeahead2',
151      dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect',
152      model: '=ngModel',
153      buttonTemplateClass: '@',
154    },
155    link: ($scope: any, elem: any, attrs: any) => {
156      const $input = $(inputTemplate);
157
158      if (!$scope.buttonTemplateClass) {
159        $scope.buttonTemplateClass = 'gf-form-input';
160      }
161
162      const $button = $(buttonTemplate);
163      const timeoutId = {
164        blur: null as any,
165      };
166      $input.appendTo(elem);
167      $button.appendTo(elem);
168
169      if (attrs.linkText) {
170        $button.html(attrs.linkText);
171      }
172
173      if (attrs.ngModel) {
174        $scope.$watch('model', (newValue: any) => {
175          each($scope.menuItems, (item) => {
176            each(item.submenu, (subItem) => {
177              if (subItem.value === newValue) {
178                $button.html(subItem.text);
179              }
180            });
181          });
182        });
183      }
184
185      const typeaheadValues = reduce(
186        $scope.menuItems,
187        (memo: any[], value, index) => {
188          if (!value.submenu) {
189            value.click = 'menuItemSelected(' + index + ')';
190            memo.push(value.text);
191          } else {
192            each(value.submenu, (item, subIndex) => {
193              item.click = 'menuItemSelected(' + index + ',' + subIndex + ')';
194              memo.push(value.text + ' ' + item.text);
195            });
196          }
197          return memo;
198        },
199        []
200      );
201
202      const closeDropdownMenu = () => {
203        $input.hide();
204        $input.val('');
205        $button.show();
206        $button.focus();
207        elem.removeClass('open');
208      };
209
210      $scope.menuItemSelected = (index: number, subIndex: number) => {
211        const menuItem = $scope.menuItems[index];
212        const payload: any = { $item: menuItem };
213        if (menuItem.submenu && subIndex !== void 0) {
214          payload.$subItem = menuItem.submenu[subIndex];
215        }
216        $scope.dropdownTypeaheadOnSelect(payload);
217        closeDropdownMenu();
218      };
219
220      $input.attr('data-provide', 'typeahead');
221      $input.typeahead({
222        source: typeaheadValues,
223        minLength: 1,
224        items: 10,
225        updater: (value: string) => {
226          const result: any = {};
227          each($scope.menuItems, (menuItem) => {
228            each(menuItem.submenu, (submenuItem) => {
229              if (value === menuItem.text + ' ' + submenuItem.text) {
230                result.$subItem = submenuItem;
231                result.$item = menuItem;
232              }
233            });
234          });
235
236          if (result.$item) {
237            $scope.$apply(() => {
238              $scope.dropdownTypeaheadOnSelect(result);
239            });
240          }
241
242          $input.trigger('blur');
243          return '';
244        },
245      });
246
247      $button.click(() => {
248        $button.hide();
249        $input.show();
250        $input.focus();
251      });
252
253      $input.keyup(() => {
254        elem.toggleClass('open', $input.val() === '');
255      });
256
257      elem.mousedown((evt: Event) => {
258        evt.preventDefault();
259        timeoutId.blur = null;
260      });
261
262      $input.blur(() => {
263        timeoutId.blur = setTimeout(() => {
264          closeDropdownMenu();
265        }, 1);
266      });
267
268      $compile(elem.contents())($scope);
269    },
270  };
271}
272
273coreModule.directive('dropdownTypeahead', dropdownTypeahead);
274coreModule.directive('dropdownTypeahead2', dropdownTypeahead2);
275