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