UNPKG

kibana-riya

Version:

Kibana is an open source (Apache Licensed), browser based analytics and search dashboard for Elasticsearch. Kibana is a snap to setup and start using. Kibana strives to be easy to get started with, while also being flexible and powerful, just like Elastic

283 lines (232 loc) 8.41 kB
import _ from 'lodash'; import rison from 'rison-node'; import keymap from 'ui/utils/key_map'; import SavedObjectsSavedObjectRegistryProvider from 'ui/saved_objects/saved_object_registry'; import uiModules from 'ui/modules'; import savedObjectFinderTemplate from 'ui/partials/saved_object_finder.html'; let module = uiModules.get('kibana'); module.directive('savedObjectFinder', function ($location, $injector, kbnUrl, Private, config) { let services = Private(SavedObjectsSavedObjectRegistryProvider).byLoaderPropertiesName; return { restrict: 'E', scope: { type: '@', title: '@?', // optional make-url attr, sets the userMakeUrl in our scope userMakeUrl: '=?makeUrl', // optional on-choose attr, sets the userOnChoose in our scope userOnChoose: '=?onChoose', // optional useLocalManagement attr, removes link to management section useLocalManagement: '=?useLocalManagement' }, template: savedObjectFinderTemplate, controllerAs: 'finder', controller: function ($scope, $element, $timeout) { let self = this; // the text input element let $input = $element.find('input[ng-model=filter]'); // The number of items to show in the list $scope.perPage = config.get('savedObjects:perPage'); // the list that will hold the suggestions let $list = $element.find('ul'); // the current filter string, used to check that returned results are still useful let currentFilter = $scope.filter; // the most recently entered search/filter let prevSearch; // the list of hits, used to render display self.hits = []; self.service = services[$scope.type]; self.properties = self.service.loaderProperties; filterResults(); /** * Boolean that keeps track of whether hits are sorted ascending (true) * or descending (false) by title * @type {Boolean} */ self.isAscending = true; /** * Sorts saved object finder hits either ascending or descending * @param {Array} hits Array of saved finder object hits * @return {Array} Array sorted either ascending or descending */ self.sortHits = function (hits) { self.isAscending = !self.isAscending; self.hits = self.isAscending ? _.sortBy(hits, 'title') : _.sortBy(hits, 'title').reverse(); }; /** * Passed the hit objects and will determine if the * hit should have a url in the UI, returns it if so * @return {string|null} - the url or nothing */ self.makeUrl = function (hit) { if ($scope.userMakeUrl) { return $scope.userMakeUrl(hit); } if (!$scope.userOnChoose) { return hit.url; } return '#'; }; self.preventClick = function ($event) { $event.preventDefault(); }; /** * Called when a hit object is clicked, can override the * url behavior if necessary. */ self.onChoose = function (hit, $event) { if ($scope.userOnChoose) { $scope.userOnChoose(hit, $event); } let url = self.makeUrl(hit); if (!url || url === '#' || url.charAt(0) !== '#') return; $event.preventDefault(); // we want the '/path', not '#/path' kbnUrl.change(url.substr(1)); }; $scope.$watch('filter', function (newFilter) { // ensure that the currentFilter changes from undefined to '' // which triggers currentFilter = newFilter || ''; filterResults(); }); //manages the state of the keyboard selector self.selector = { enabled: false, index: -1 }; //key handler for the filter text box self.filterKeyDown = function ($event) { switch (keymap[$event.keyCode]) { case 'tab': if (self.hitCount === 0) return; self.selector.index = 0; self.selector.enabled = true; selectTopHit(); $event.preventDefault(); break; case 'enter': if (self.hitCount !== 1) return; let hit = self.hits[0]; if (!hit) return; self.onChoose(hit, $event); $event.preventDefault(); break; } }; //key handler for the list items self.hitKeyDown = function ($event, page, paginate) { switch (keymap[$event.keyCode]) { case 'tab': if (!self.selector.enabled) break; self.selector.index = -1; self.selector.enabled = false; //if the user types shift-tab return to the textbox //if the user types tab, set the focus to the currently selected hit. if ($event.shiftKey) { $input.focus(); } else { $list.find('li.active a').focus(); } $event.preventDefault(); break; case 'down': if (!self.selector.enabled) break; if (self.selector.index + 1 < page.length) { self.selector.index += 1; } $event.preventDefault(); break; case 'up': if (!self.selector.enabled) break; if (self.selector.index > 0) { self.selector.index -= 1; } $event.preventDefault(); break; case 'right': if (!self.selector.enabled) break; if (page.number < page.count) { paginate.goToPage(page.number + 1); self.selector.index = 0; selectTopHit(); } $event.preventDefault(); break; case 'left': if (!self.selector.enabled) break; if (page.number > 1) { paginate.goToPage(page.number - 1); self.selector.index = 0; selectTopHit(); } $event.preventDefault(); break; case 'escape': if (!self.selector.enabled) break; $input.focus(); $event.preventDefault(); break; case 'enter': if (!self.selector.enabled) break; let hitIndex = ((page.number - 1) * paginate.perPage) + self.selector.index; let hit = self.hits[hitIndex]; if (!hit) break; self.onChoose(hit, $event); $event.preventDefault(); break; case 'shift': break; default: $input.focus(); break; } }; self.hitBlur = function ($event) { self.selector.index = -1; self.selector.enabled = false; }; self.manageObjects = function (type) { $location.url('/management/kibana/objects?_a=' + rison.encode({tab: type})); }; self.hitCountNoun = function () { return ((self.hitCount === 1) ? self.properties.noun : self.properties.nouns).toLowerCase(); }; function selectTopHit() { setTimeout(function () { //triggering a focus event kicks off a new angular digest cycle. $list.find('a:first').focus(); }, 0); } function filterResults() { if (!self.service) return; if (!self.properties) return; // track the filter that we use for this search, // but ensure that we don't search for the same // thing twice. This is called from multiple places // and needs to be smart about when it actually searches let filter = currentFilter; if (prevSearch === filter) return; prevSearch = filter; self.service.find(filter) .then(function (hits) { // ensure that we don't display old results // as we can't really cancel requests if (currentFilter === filter) { self.hitCount = hits.total; self.hits = _.sortBy(hits.hits, 'title'); } }); } function scrollIntoView($element, snapTop) { let el = $element[0]; if (!el) return; if ('scrollIntoViewIfNeeded' in el) { el.scrollIntoViewIfNeeded(snapTop); } else if ('scrollIntoView' in el) { el.scrollIntoView(snapTop); } } } }; });