diff --git a/public/app/core/directives/dropdown_typeahead.ts b/public/app/core/directives/dropdown_typeahead.ts new file mode 100644 index 00000000000..03f80f10ca7 --- /dev/null +++ b/public/app/core/directives/dropdown_typeahead.ts @@ -0,0 +1,244 @@ +import _ from 'lodash'; +import $ from 'jquery'; +import coreModule from '../core_module'; + +/** @ngInject */ +export function dropdownTypeahead($compile) { + let inputTemplate = + ''; + + let buttonTemplate = + ''; + + return { + scope: { + menuItems: '=dropdownTypeahead', + dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect', + model: '=ngModel', + }, + link: function($scope, elem, attrs) { + let $input = $(inputTemplate); + let $button = $(buttonTemplate); + $input.appendTo(elem); + $button.appendTo(elem); + + if (attrs.linkText) { + $button.html(attrs.linkText); + } + + if (attrs.ngModel) { + $scope.$watch('model', function(newValue) { + _.each($scope.menuItems, function(item) { + _.each(item.submenu, function(subItem) { + if (subItem.value === newValue) { + $button.html(subItem.text); + } + }); + }); + }); + } + + let typeaheadValues = _.reduce( + $scope.menuItems, + function(memo, value, index) { + if (!value.submenu) { + value.click = 'menuItemSelected(' + index + ')'; + memo.push(value.text); + } else { + _.each(value.submenu, function(item, subIndex) { + item.click = 'menuItemSelected(' + index + ',' + subIndex + ')'; + memo.push(value.text + ' ' + item.text); + }); + } + return memo; + }, + [] + ); + + $scope.menuItemSelected = function(index, subIndex) { + let menuItem = $scope.menuItems[index]; + let payload: any = { $item: menuItem }; + if (menuItem.submenu && subIndex !== void 0) { + payload.$subItem = menuItem.submenu[subIndex]; + } + $scope.dropdownTypeaheadOnSelect(payload); + }; + + $input.attr('data-provide', 'typeahead'); + $input.typeahead({ + source: typeaheadValues, + minLength: 1, + items: 10, + updater: function(value) { + let result: any = {}; + _.each($scope.menuItems, function(menuItem) { + _.each(menuItem.submenu, function(submenuItem) { + if (value === menuItem.text + ' ' + submenuItem.text) { + result.$subItem = submenuItem; + result.$item = menuItem; + } + }); + }); + + if (result.$item) { + $scope.$apply(function() { + $scope.dropdownTypeaheadOnSelect(result); + }); + } + + $input.trigger('blur'); + return ''; + }, + }); + + $button.click(function() { + $button.hide(); + $input.show(); + $input.focus(); + }); + + $input.keyup(function() { + elem.toggleClass('open', $input.val() === ''); + }); + + $input.blur(function() { + $input.hide(); + $input.val(''); + $button.show(); + $button.focus(); + // clicking the function dropdown menu wont + // work if you remove class at once + setTimeout(function() { + elem.removeClass('open'); + }, 200); + }); + + $compile(elem.contents())($scope); + }, + }; +} + +/** @ngInject */ +export function dropdownTypeahead2($compile) { + let inputTemplate = + ''; + + let buttonTemplate = + ''; + + return { + scope: { + menuItems: '=dropdownTypeahead2', + dropdownTypeaheadOnSelect: '&dropdownTypeaheadOnSelect', + model: '=ngModel', + }, + link: function($scope, elem, attrs) { + let $input = $(inputTemplate); + let $button = $(buttonTemplate); + $input.appendTo(elem); + $button.appendTo(elem); + + if (attrs.linkText) { + $button.html(attrs.linkText); + } + + if (attrs.ngModel) { + $scope.$watch('model', function(newValue) { + _.each($scope.menuItems, function(item) { + _.each(item.submenu, function(subItem) { + if (subItem.value === newValue) { + $button.html(subItem.text); + } + }); + }); + }); + } + + let typeaheadValues = _.reduce( + $scope.menuItems, + function(memo, value, index) { + if (!value.submenu) { + value.click = 'menuItemSelected(' + index + ')'; + memo.push(value.text); + } else { + _.each(value.submenu, function(item, subIndex) { + item.click = 'menuItemSelected(' + index + ',' + subIndex + ')'; + memo.push(value.text + ' ' + item.text); + }); + } + return memo; + }, + [] + ); + + $scope.menuItemSelected = function(index, subIndex) { + let menuItem = $scope.menuItems[index]; + let payload: any = { $item: menuItem }; + if (menuItem.submenu && subIndex !== void 0) { + payload.$subItem = menuItem.submenu[subIndex]; + } + $scope.dropdownTypeaheadOnSelect(payload); + }; + + $input.attr('data-provide', 'typeahead'); + $input.typeahead({ + source: typeaheadValues, + minLength: 1, + items: 10, + updater: function(value) { + let result: any = {}; + _.each($scope.menuItems, function(menuItem) { + _.each(menuItem.submenu, function(submenuItem) { + if (value === menuItem.text + ' ' + submenuItem.text) { + result.$subItem = submenuItem; + result.$item = menuItem; + } + }); + }); + + if (result.$item) { + $scope.$apply(function() { + $scope.dropdownTypeaheadOnSelect(result); + }); + } + + $input.trigger('blur'); + return ''; + }, + }); + + $button.click(function() { + $button.hide(); + $input.show(); + $input.focus(); + }); + + $input.keyup(function() { + elem.toggleClass('open', $input.val() === ''); + }); + + $input.blur(function() { + $input.hide(); + $input.val(''); + $button.show(); + $button.focus(); + // clicking the function dropdown menu wont + // work if you remove class at once + setTimeout(function() { + elem.removeClass('open'); + }, 200); + }); + + $compile(elem.contents())($scope); + }, + }; +} + +coreModule.directive('dropdownTypeahead', dropdownTypeahead); +coreModule.directive('dropdownTypeahead2', dropdownTypeahead2);