+
+
+
`;
+
+export class OrgSwitchCtrl {
+ orgs: any[];
+ currentOrgId: any;
+
+ /** @ngInject */
+ constructor(private backendSrv) {
+ this.currentOrgId = contextSrv.user.orgId;
+ this.getUserOrgs();
+ }
+
+ getUserOrgs() {
+ this.backendSrv.get('/api/user/orgs').then(orgs => {
+ this.orgs = orgs;
+ });
+ }
+
+ setUsingOrg(org) {
+ return this.backendSrv.post('/api/user/using/' + org.orgId).then(() => {
+ const re = /orgId=\d+/gi;
+ this.setWindowLocationHref(this.getWindowLocationHref().replace(re, 'orgId=' + org.orgId));
+ });
+ }
+
+ getWindowLocationHref() {
+ return window.location.href;
+ }
+
+ setWindowLocationHref(href: string) {
+ window.location.href = href;
+ }
+}
+
+export function orgSwitcher() {
+ return {
+ restrict: 'E',
+ template: template,
+ controller: OrgSwitchCtrl,
+ bindToController: true,
+ controllerAs: 'ctrl',
+ scope: {dismiss: "&"},
+ };
+}
+
+coreModule.directive('orgSwitcher', orgSwitcher);
diff --git a/public/app/core/components/scroll/scroll.ts b/public/app/core/components/scroll/scroll.ts
new file mode 100644
index 00000000000..54c84e2df11
--- /dev/null
+++ b/public/app/core/components/scroll/scroll.ts
@@ -0,0 +1,27 @@
+import PerfectScrollbar from 'perfect-scrollbar';
+import coreModule from 'app/core/core_module';
+
+export function geminiScrollbar() {
+ return {
+ restrict: 'A',
+ link: function(scope, elem, attrs) {
+
+ let scrollbar = new PerfectScrollbar(elem[0]);
+
+ scope.$on('$routeChangeSuccess', () => {
+ elem[0].scrollTop = 0;
+ });
+
+ scope.$on('$routeUpdate', () => {
+ elem[0].scrollTop = 0;
+ });
+
+ scope.$on('$destroy', () => {
+ scrollbar.destroy();
+ });
+
+ }
+ };
+}
+
+coreModule.directive('grafanaScrollbar', geminiScrollbar);
diff --git a/public/app/core/components/search/search.html b/public/app/core/components/search/search.html
index ca1bd9406bf..03d2a66108c 100644
--- a/public/app/core/components/search/search.html
+++ b/public/app/core/components/search/search.html
@@ -4,9 +4,7 @@
-
-
+
+
+
+
No dashboards matching your query were found.
+
+
+
-
-
No dashboards matching your query were found.
+
-
-
-
+
+
+
diff --git a/public/app/core/components/search/search.ts b/public/app/core/components/search/search.ts
index ee964e1fe6e..ffbec30cc0a 100644
--- a/public/app/core/components/search/search.ts
+++ b/public/app/core/components/search/search.ts
@@ -1,8 +1,6 @@
-///
-
-import config from 'app/core/config';
import _ from 'lodash';
import coreModule from '../../core_module';
+import { SearchSrv } from 'app/core/services/search_srv';
export class SearchCtrl {
isOpen: boolean;
@@ -11,27 +9,27 @@ export class SearchCtrl {
selectedIndex: number;
results: any;
currentSearchId: number;
- tagsMode: boolean;
showImport: boolean;
dismiss: any;
ignoreClose: any;
- // triggers fade animation class
- openCompleted: boolean;
+ isLoading: boolean;
+ initialFolderFilterTitle: string;
/** @ngInject */
- constructor($scope, private $location, private $timeout, private backendSrv, private dashboardSrv, public contextSrv, $rootScope) {
+ constructor($scope, private $location, private $timeout, private searchSrv: SearchSrv, $rootScope) {
$rootScope.onAppEvent('show-dash-search', this.openSearch.bind(this), $scope);
$rootScope.onAppEvent('hide-dash-search', this.closeSearch.bind(this), $scope);
+
+ this.initialFolderFilterTitle = "All";
}
closeSearch() {
this.isOpen = this.ignoreClose;
- this.openCompleted = false;
}
openSearch(evt, payload) {
if (this.isOpen) {
- this.isOpen = false;
+ this.closeSearch();
return;
}
@@ -42,21 +40,13 @@ export class SearchCtrl {
this.query = { query: '', tag: [], starred: false };
this.currentSearchId = 0;
this.ignoreClose = true;
+ this.isLoading = true;
if (payload && payload.starred) {
this.query.starred = true;
}
- if (payload && payload.tagsMode) {
- return this.$timeout(() => {
- this.ignoreClose = false;
- this.giveSearchFocus = this.giveSearchFocus + 1;
- this.getTags();
- }, 100);
- }
-
this.$timeout(() => {
- this.openCompleted = true;
this.ignoreClose = false;
this.giveSearchFocus = this.giveSearchFocus + 1;
this.search();
@@ -74,44 +64,86 @@ export class SearchCtrl {
this.moveSelection(-1);
}
if (evt.keyCode === 13) {
- if (this.tagsMode) {
- var tag = this.results[this.selectedIndex];
- if (tag) {
- this.filterByTag(tag.term, null);
- }
- return;
- }
+ const flattenedResult = this.getFlattenedResultForNavigation();
+ const currentItem = flattenedResult[this.selectedIndex];
- var selectedDash = this.results[this.selectedIndex];
- if (selectedDash) {
- this.$location.search({});
- this.$location.path(selectedDash.url);
+ if (currentItem) {
+ if (currentItem.dashboardIndex !== undefined) {
+ const selectedDash = this.results[currentItem.folderIndex].items[currentItem.dashboardIndex];
+
+ if (selectedDash) {
+ this.$location.search({});
+ this.$location.path(selectedDash.url);
+ }
+ } else {
+ const selectedFolder = this.results[currentItem.folderIndex];
+
+ if (selectedFolder) {
+ selectedFolder.toggle(selectedFolder);
+ }
+ }
}
}
}
moveSelection(direction) {
- var max = (this.results || []).length;
- var newIndex = this.selectedIndex + direction;
+ if (this.results.length === 0) {
+ return;
+ }
+
+ const flattenedResult = this.getFlattenedResultForNavigation();
+ const currentItem = flattenedResult[this.selectedIndex];
+
+ if (currentItem) {
+ if (currentItem.dashboardIndex !== undefined) {
+ this.results[currentItem.folderIndex].items[currentItem.dashboardIndex].selected = false;
+ } else {
+ this.results[currentItem.folderIndex].selected = false;
+ }
+ }
+
+ if (direction === 0) {
+ this.selectedIndex = -1;
+ return;
+ }
+
+ const max = flattenedResult.length;
+ let newIndex = this.selectedIndex + direction;
this.selectedIndex = ((newIndex %= max) < 0) ? newIndex + max : newIndex;
+ const selectedItem = flattenedResult[this.selectedIndex];
+
+ if (selectedItem.dashboardIndex === undefined && this.results[selectedItem.folderIndex].id === 0) {
+ this.moveSelection(direction);
+ return;
+ }
+
+ if (selectedItem.dashboardIndex !== undefined) {
+ if (!this.results[selectedItem.folderIndex].expanded) {
+ this.moveSelection(direction);
+ return;
+ }
+
+ this.results[selectedItem.folderIndex].items[selectedItem.dashboardIndex].selected = true;
+ return;
+ }
+
+ if (this.results[selectedItem.folderIndex].hideHeader) {
+ this.moveSelection(direction);
+ return;
+ }
+
+ this.results[selectedItem.folderIndex].selected = true;
}
searchDashboards() {
- this.tagsMode = false;
this.currentSearchId = this.currentSearchId + 1;
var localSearchId = this.currentSearchId;
- return this.backendSrv.search(this.query).then((results) => {
+ return this.searchSrv.search(this.query).then(results => {
if (localSearchId < this.currentSearchId) { return; }
-
- this.results = _.map(results, function(dash) {
- dash.url = 'dashboard/' + dash.uri;
- return dash;
- });
-
- if (this.queryHasNoFilters()) {
- this.results.unshift({ title: 'Home', url: config.appSubUrl + '/', type: 'dash-home' });
- }
+ this.results = results || [];
+ this.isLoading = false;
+ this.moveSelection(1);
});
}
@@ -120,13 +152,11 @@ export class SearchCtrl {
return query.query === '' && query.starred === false && query.tag.length === 0;
}
- filterByTag(tag, evt) {
- this.query.tag.push(tag);
- this.search();
- this.giveSearchFocus = this.giveSearchFocus + 1;
- if (evt) {
- evt.stopPropagation();
- evt.preventDefault();
+ filterByTag(tag) {
+ if (_.indexOf(this.query.tag, tag) === -1) {
+ this.query.tag.push(tag);
+ this.search();
+ this.giveSearchFocus = this.giveSearchFocus + 1;
}
}
@@ -139,13 +169,9 @@ export class SearchCtrl {
}
getTags() {
- return this.backendSrv.get('/api/dashboards/tags').then((results) => {
- this.tagsMode = !this.tagsMode;
+ return this.searchSrv.getDashboardTags().then((results) => {
this.results = results;
this.giveSearchFocus = this.giveSearchFocus + 1;
- if ( !this.tagsMode ) {
- this.search();
- }
});
}
@@ -157,18 +183,36 @@ export class SearchCtrl {
search() {
this.showImport = false;
- this.selectedIndex = 0;
+ this.selectedIndex = -1;
this.searchDashboards();
}
- starDashboard(row, evt) {
- this.dashboardSrv.starDashboard(row.id, row.isStarred).then(newState => {
- row.isStarred = newState;
+ folderExpanding() {
+ this.moveSelection(0);
+ }
+
+ private getFlattenedResultForNavigation() {
+ let folderIndex = 0;
+
+ return _.flatMap(this.results, (s) => {
+ let result = [];
+
+ result.push({
+ folderIndex: folderIndex
+ });
+
+ let dashboardIndex = 0;
+
+ result = result.concat(_.map(s.items || [], (i) => {
+ return {
+ folderIndex: folderIndex,
+ dashboardIndex: dashboardIndex++
+ };
+ }));
+
+ folderIndex++;
+ return result;
});
- if (evt) {
- evt.stopPropagation();
- evt.preventDefault();
- }
}
}
diff --git a/public/app/core/components/search/search_results.html b/public/app/core/components/search/search_results.html
new file mode 100644
index 00000000000..a776dabe0dc
--- /dev/null
+++ b/public/app/core/components/search/search_results.html
@@ -0,0 +1,46 @@
+
+
diff --git a/public/app/core/components/search/search_results.ts b/public/app/core/components/search/search_results.ts
new file mode 100644
index 00000000000..3e7e8e6147b
--- /dev/null
+++ b/public/app/core/components/search/search_results.ts
@@ -0,0 +1,93 @@
+import _ from 'lodash';
+import coreModule from '../../core_module';
+
+export class SearchResultsCtrl {
+ results: any;
+ onSelectionChanged: any;
+ onTagSelected: any;
+ onFolderExpanding: any;
+ editable: boolean;
+
+ /** @ngInject */
+ constructor(private $location) {}
+
+ toggleFolderExpand(section) {
+ if (section.toggle) {
+ if (!section.expanded && this.onFolderExpanding) {
+ this.onFolderExpanding();
+ }
+
+ section.toggle(section).then(f => {
+ if (this.editable && f.expanded) {
+ if (f.items) {
+ _.each(f.items, i => {
+ i.checked = f.checked;
+ });
+
+ if (this.onSelectionChanged) {
+ this.onSelectionChanged();
+ }
+ }
+ }
+ });
+ }
+ }
+
+ navigateToFolder(section, evt) {
+ this.$location.path(section.url);
+
+ if (evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ }
+ }
+
+ toggleSelection(item, evt) {
+ item.checked = !item.checked;
+
+ if (item.items) {
+ _.each(item.items, i => {
+ i.checked = item.checked;
+ });
+ }
+
+ if (this.onSelectionChanged) {
+ this.onSelectionChanged();
+ }
+
+ if (evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ }
+ }
+
+ selectTag(tag, evt) {
+ if (this.onTagSelected) {
+ this.onTagSelected({$tag: tag});
+ }
+
+ if (evt) {
+ evt.stopPropagation();
+ evt.preventDefault();
+ }
+ }
+}
+
+export function searchResultsDirective() {
+ return {
+ restrict: 'E',
+ templateUrl: 'public/app/core/components/search/search_results.html',
+ controller: SearchResultsCtrl,
+ bindToController: true,
+ controllerAs: 'ctrl',
+ scope: {
+ editable: '@',
+ results: '=',
+ onSelectionChanged: '&',
+ onTagSelected: '&',
+ onFolderExpanding: '&'
+ },
+ };
+}
+
+coreModule.directive('dashboardSearchResults', searchResultsDirective);
diff --git a/public/app/core/components/sidemenu/sidemenu.html b/public/app/core/components/sidemenu/sidemenu.html
index 8aa3c6ae571..e97d34739ba 100644
--- a/public/app/core/components/sidemenu/sidemenu.html
+++ b/public/app/core/components/sidemenu/sidemenu.html
@@ -1,50 +1,24 @@
-