mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
New TV Mode, dashboard toolbar update (layout change & new cycle view mode button) (#13025)
* wip: design update for navbar with kiosk mode button * feat: progress on new view mode button * css: view state refactorings * feat: kiosk modes & playlist support * feature: cycle tv mode feature, renamed view modes to TV, and Kiosk * fix: updated the alert notification message * fix: removed unused parameter * fix: correct the css class set for tv mode * some minor improvements to playlist
This commit is contained in:
parent
36bc8b77fb
commit
154fbe2413
@ -69,6 +69,30 @@ export class GrafanaCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
function setViewModeBodyClass(body, mode, sidemenuOpen: boolean) {
|
||||
body.removeClass('view-mode--tv');
|
||||
body.removeClass('view-mode--kiosk');
|
||||
body.removeClass('view-mode--inactive');
|
||||
|
||||
switch (mode) {
|
||||
case 'tv': {
|
||||
body.removeClass('sidemenu-open');
|
||||
body.addClass('view-mode--tv');
|
||||
break;
|
||||
}
|
||||
// 1 & true for legacy states
|
||||
case 1:
|
||||
case true: {
|
||||
body.removeClass('sidemenu-open');
|
||||
body.addClass('view-mode--kiosk');
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
body.toggleClass('sidemenu-open', sidemenuOpen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @ngInject */
|
||||
export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScope, $location) {
|
||||
return {
|
||||
@ -98,7 +122,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
});
|
||||
|
||||
scope.$watch(() => playlistSrv.isPlaying, function(newValue) {
|
||||
elem.toggleClass('playlist-active', newValue === true);
|
||||
elem.toggleClass('view-mode--playlist', newValue === true);
|
||||
});
|
||||
|
||||
// check if we are in server side render
|
||||
@ -127,17 +151,7 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
$('#tooltip, .tooltip').remove();
|
||||
|
||||
// check for kiosk url param
|
||||
if (data.params.kiosk) {
|
||||
appEvents.emit('toggle-kiosk-mode');
|
||||
}
|
||||
|
||||
// check for 'inactive' url param for clean looks like kiosk, but with title
|
||||
if (data.params.inactive) {
|
||||
body.addClass('user-activity-low');
|
||||
|
||||
// for some reason, with this class it looks cleanest
|
||||
body.addClass('sidemenu-open');
|
||||
}
|
||||
setViewModeBodyClass(body, data.params.kiosk, sidemenuOpen);
|
||||
|
||||
// close all drops
|
||||
for (const drop of Drop.drops) {
|
||||
@ -146,15 +160,37 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
});
|
||||
|
||||
// handle kiosk mode
|
||||
appEvents.on('toggle-kiosk-mode', () => {
|
||||
body.toggleClass('page-kiosk-mode');
|
||||
appEvents.on('toggle-kiosk-mode', options => {
|
||||
const search = $location.search();
|
||||
|
||||
if (options && options.exit) {
|
||||
search.kiosk = 1;
|
||||
}
|
||||
|
||||
switch (search.kiosk) {
|
||||
case 'tv': {
|
||||
search.kiosk = 1;
|
||||
appEvents.emit('alert-success', ['Press ESC to exit Kiosk mode']);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
case true: {
|
||||
delete search.kiosk;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
search.kiosk = 'tv';
|
||||
}
|
||||
}
|
||||
|
||||
$location.search(search);
|
||||
setViewModeBodyClass(body, search.kiosk, sidemenuOpen);
|
||||
});
|
||||
|
||||
// handle in active view state class
|
||||
let lastActivity = new Date().getTime();
|
||||
let activeUser = true;
|
||||
const inActiveTimeLimit = 60 * 1000;
|
||||
let sidemenuHidden = false;
|
||||
const inActiveTimeLimit = 60 * 5000;
|
||||
|
||||
function checkForInActiveUser() {
|
||||
if (!activeUser) {
|
||||
@ -167,15 +203,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
|
||||
if (new Date().getTime() - lastActivity > inActiveTimeLimit) {
|
||||
activeUser = false;
|
||||
body.addClass('user-activity-low');
|
||||
// hide sidemenu
|
||||
if (sidemenuOpen) {
|
||||
sidemenuHidden = true;
|
||||
body.removeClass('sidemenu-open');
|
||||
$timeout(function() {
|
||||
$rootScope.$broadcast('render');
|
||||
}, 100);
|
||||
}
|
||||
body.addClass('view-mode--inactive');
|
||||
body.removeClass('sidemenu-open');
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,17 +212,8 @@ export function grafanaAppDirective(playlistSrv, contextSrv, $timeout, $rootScop
|
||||
lastActivity = new Date().getTime();
|
||||
if (!activeUser) {
|
||||
activeUser = true;
|
||||
body.removeClass('user-activity-low');
|
||||
|
||||
// restore sidemenu
|
||||
if (sidemenuHidden) {
|
||||
sidemenuHidden = false;
|
||||
body.addClass('sidemenu-open');
|
||||
appEvents.emit('toggle-inactive-mode');
|
||||
$timeout(function() {
|
||||
$rootScope.$broadcast('render');
|
||||
}, 100);
|
||||
}
|
||||
body.removeClass('view-mode--inactive');
|
||||
body.toggleClass('sidemenu-open', sidemenuOpen);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import $ from 'jquery';
|
||||
import baron from 'baron';
|
||||
import coreModule from 'app/core/core_module';
|
||||
import appEvents from 'app/core/app_events';
|
||||
|
||||
const scrollBarHTML = `
|
||||
<div class="baron__track">
|
||||
@ -39,43 +38,6 @@ export function geminiScrollbar() {
|
||||
|
||||
const scrollbar = baron(scrollParams);
|
||||
|
||||
let lastPos = 0;
|
||||
|
||||
appEvents.on(
|
||||
'dash-scroll',
|
||||
evt => {
|
||||
if (evt.restore) {
|
||||
elem[0].scrollTop = lastPos;
|
||||
return;
|
||||
}
|
||||
|
||||
lastPos = elem[0].scrollTop;
|
||||
|
||||
if (evt.animate) {
|
||||
elem.animate({ scrollTop: evt.pos }, 500);
|
||||
} else {
|
||||
elem[0].scrollTop = evt.pos;
|
||||
}
|
||||
},
|
||||
scope
|
||||
);
|
||||
|
||||
// force updating dashboard width
|
||||
appEvents.on('toggle-sidemenu', forceUpdate, scope);
|
||||
appEvents.on('toggle-sidemenu-hidden', forceUpdate, scope);
|
||||
appEvents.on('toggle-view-mode', forceUpdate, scope);
|
||||
appEvents.on('toggle-kiosk-mode', forceUpdate, scope);
|
||||
appEvents.on('toggle-inactive-mode', forceUpdate, scope);
|
||||
|
||||
function forceUpdate() {
|
||||
scrollbar.scroll();
|
||||
}
|
||||
|
||||
scope.$on('$routeChangeSuccess', () => {
|
||||
lastPos = 0;
|
||||
elem[0].scrollTop = 0;
|
||||
});
|
||||
|
||||
scope.$on('$destroy', () => {
|
||||
scrollbar.dispose();
|
||||
});
|
||||
|
@ -77,15 +77,15 @@ export class KeybindingSrv {
|
||||
|
||||
appEvents.emit('hide-modal');
|
||||
|
||||
if (!this.modalOpen) {
|
||||
if (this.timepickerOpen) {
|
||||
this.$rootScope.appEvent('closeTimepicker');
|
||||
this.timepickerOpen = false;
|
||||
} else {
|
||||
this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||
}
|
||||
} else {
|
||||
if (this.modalOpen) {
|
||||
this.modalOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.timepickerOpen) {
|
||||
this.$rootScope.appEvent('closeTimepicker');
|
||||
this.timepickerOpen = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// close settings view
|
||||
@ -93,6 +93,16 @@ export class KeybindingSrv {
|
||||
if (search.editview) {
|
||||
delete search.editview;
|
||||
this.$location.search(search);
|
||||
return;
|
||||
}
|
||||
|
||||
if (search.fullscreen) {
|
||||
this.$rootScope.appEvent('panel-change-view', { fullscreen: false, edit: false });
|
||||
return;
|
||||
}
|
||||
|
||||
if (search.kiosk) {
|
||||
this.$rootScope.appEvent('toggle-kiosk-mode', { exit: true });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -842,12 +842,20 @@ export class DashboardModel {
|
||||
})
|
||||
);
|
||||
|
||||
// Consider navbar and submenu controls, padding and margin
|
||||
let visibleHeight = window.innerHeight - 55 - 20;
|
||||
const navbarHeight = 55;
|
||||
const margin = 20;
|
||||
const submenuHeight = 50;
|
||||
|
||||
// Remove submenu if visible
|
||||
if (this.meta.submenuEnabled) {
|
||||
visibleHeight -= 50;
|
||||
let visibleHeight = viewHeight - navbarHeight - margin;
|
||||
|
||||
// Remove submenu height if visible
|
||||
if (this.meta.submenuEnabled && !this.meta.kiosk) {
|
||||
visibleHeight -= submenuHeight;
|
||||
}
|
||||
|
||||
// add back navbar height
|
||||
if (this.meta.kiosk === 'b') {
|
||||
visibleHeight += 55;
|
||||
}
|
||||
|
||||
const visibleGridHeight = Math.floor(visibleHeight / (GRID_CELL_HEIGHT + GRID_CELL_VMARGIN));
|
||||
|
@ -8,14 +8,14 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar__spacer"></div>
|
||||
|
||||
<div class="navbar-buttons navbar-buttons--playlist" ng-if="ctrl.playlistSrv.isPlaying">
|
||||
<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
|
||||
<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
|
||||
<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
|
||||
</div>
|
||||
|
||||
<div class="navbar__spacer"></div>
|
||||
|
||||
<div class="navbar-buttons navbar-buttons--actions">
|
||||
<button class="btn navbar-button navbar-button--add-panel" ng-show="::ctrl.dashboard.meta.canSave" bs-tooltip="'Add panel'" data-placement="bottom" ng-click="ctrl.addPanel()">
|
||||
<i class="gicon gicon-add-panel"></i>
|
||||
@ -25,11 +25,11 @@
|
||||
<i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button navbar-button--share" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
|
||||
<button class="btn navbar-button navbar-button--share" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
|
||||
<i class="fa fa-share-square-o"></i></a>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button navbar-button--save" ng-show="ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
|
||||
<button class="btn navbar-button navbar-button--save" ng-show="ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
|
||||
<i class="fa fa-save"></i>
|
||||
</button>
|
||||
|
||||
@ -42,6 +42,12 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-buttons navbar-buttons--tv">
|
||||
<button class="btn navbar-button navbar-button--tv" ng-click="ctrl.toggleViewMode()" bs-tooltip="'Cycle view mode'" data-placement="bottom">
|
||||
<i class="fa fa-desktop"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<gf-time-picker class="gf-timepicker-nav" dashboard="ctrl.dashboard" ng-if="!ctrl.dashboard.timepicker.hidden"></gf-time-picker>
|
||||
|
||||
<div class="navbar-buttons navbar-buttons--close">
|
||||
|
@ -31,6 +31,10 @@ export class DashNavCtrl {
|
||||
this.$location.search(search);
|
||||
}
|
||||
|
||||
toggleViewMode() {
|
||||
appEvents.emit('toggle-kiosk-mode');
|
||||
}
|
||||
|
||||
close() {
|
||||
const search = this.$location.search();
|
||||
if (search.editview) {
|
||||
|
@ -1,18 +1,8 @@
|
||||
<div class="navbar-buttons navbar-buttons--zoom">
|
||||
<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(-1)'>
|
||||
<div class="navbar-buttons">
|
||||
<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(-1)' ng-if="ctrl.isAbsolute">
|
||||
<i class="fa fa-chevron-left"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button" bs-tooltip="'Time range zoom out <br> CTRL+Z'" data-placement="bottom" ng-click='ctrl.zoom(2)'>
|
||||
<i class="fa fa-search-minus"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(1)'>
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="navbar-buttons">
|
||||
<button bs-tooltip="ctrl.tooltip" data-placement="bottom" ng-click="ctrl.openDropdown()" class="btn navbar-button gf-timepicker-nav-btn">
|
||||
<i class="fa fa-clock-o"></i>
|
||||
<span ng-bind="ctrl.rangeString"></span>
|
||||
@ -20,7 +10,15 @@
|
||||
<span ng-show="ctrl.dashboard.refresh" class="text-warning"> Refresh every {{ctrl.dashboard.refresh}}</span>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button navbar-button--refresh" ng-click="ctrl.timeSrv.refreshDashboard()">
|
||||
<button class="btn navbar-button navbar-button--tight" ng-click='ctrl.move(1)' ng-if="ctrl.isAbsolute">
|
||||
<i class="fa fa-chevron-right"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button navbar-button--zoom" bs-tooltip="'Time range zoom out <br> CTRL+Z'" data-placement="bottom" ng-click='ctrl.zoom(2)'>
|
||||
<i class="fa fa-search-minus"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn navbar-button navbar-button--refresh" ng-click="ctrl.timeSrv.refreshDashboard()">
|
||||
<i class="fa fa-refresh"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -23,6 +23,7 @@ export class TimePickerCtrl {
|
||||
isUtc: boolean;
|
||||
firstDayOfWeek: number;
|
||||
isOpen: boolean;
|
||||
isAbsolute: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private $rootScope, private timeSrv) {
|
||||
@ -65,6 +66,7 @@ export class TimePickerCtrl {
|
||||
this.tooltip = this.dashboard.formatDate(time.from) + ' <br>to<br>';
|
||||
this.tooltip += this.dashboard.formatDate(time.to);
|
||||
this.timeRaw = timeRaw;
|
||||
this.isAbsolute = moment.isMoment(this.timeRaw.to);
|
||||
}
|
||||
|
||||
zoom(factor) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
<page-header model="ctrl.navModel"></page-header>
|
||||
|
||||
<div class="page-container page-body" ng-form="playlistEditForm">
|
||||
<div class="page-container page-body" ng-form="ctrl.playlistEditForm">
|
||||
|
||||
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
|
||||
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
|
||||
<h3 class="page-sub-heading" ng-hide="ctrl.isNew">Edit Playlist</h3>
|
||||
<h3 class="page-sub-heading" ng-show="ctrl.isNew">New Playlist</h3>
|
||||
|
||||
<p class="playlist-description">A playlist rotates through a pre-selected list of Dashboards. A Playlist can be a great way to build situational awareness, or just show off your metrics to your team or visitors.</p>
|
||||
|
||||
@ -20,79 +20,71 @@
|
||||
|
||||
<div class="gf-form-group">
|
||||
<h3 class="page-headering">Dashboards</h3>
|
||||
|
||||
<table class="filter-table playlist-available-list">
|
||||
<tr ng-repeat="playlistItem in ctrl.playlistItems">
|
||||
<td ng-if="playlistItem.type === 'dashboard_by_id'">
|
||||
<i class="icon-gf icon-gf-dashboard"></i> {{playlistItem.title}}
|
||||
</td>
|
||||
<td ng-if="playlistItem.type === 'dashboard_by_tag'">
|
||||
<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span>{{playlistItem.title}}</span>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td class="selected-playlistitem-settings">
|
||||
<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
|
||||
<i class="fa fa-arrow-up"></i>
|
||||
</button>
|
||||
<button class="btn btn-inverse btn-mini" ng-hide="$last" ng-click="ctrl.movePlaylistItemDown(playlistItem)">
|
||||
<i class="fa fa-arrow-down"></i>
|
||||
</button>
|
||||
<button class="btn btn-inverse btn-mini" ng-click="ctrl.removePlaylistItem(playlistItem)">
|
||||
<i class="fa fa-remove"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="ctrl.playlistItems.length === 0">
|
||||
<td><em>Playlist is empty, add dashboards below.</em></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="playlist-search-containerwrapper">
|
||||
<div class="max-width-32">
|
||||
<h5 class="page-headering playlist-column-header">Available</h5>
|
||||
<div style="">
|
||||
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form-group">
|
||||
<h3 class="page-headering">Add dashboards</h3>
|
||||
<playlist-search class="playlist-search-container" search-started="ctrl.searchStarted(promise)"></playlist-search>
|
||||
|
||||
<div ng-if="ctrl.filteredDashboards.length > 0">
|
||||
<table class="filter-table playlist-available-list">
|
||||
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
|
||||
<td>
|
||||
<i class="icon-gf icon-gf-dashboard"></i>
|
||||
{{playlistItem.title}}
|
||||
<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
|
||||
</td>
|
||||
<td class="add-dashboard">
|
||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add to playlist
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
|
||||
<table class="filter-table playlist-available-list">
|
||||
<tr ng-repeat="tag in ctrl.filteredTags">
|
||||
<td>
|
||||
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span>{{tag.term}} ({{tag.count}})</span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="add-dashboard">
|
||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addTagPlaylistItem(tag)">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add to playlist
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<h5 class="page headering playlist-column-header">Selected</h5>
|
||||
<div ng-if="ctrl.filteredDashboards.length > 0">
|
||||
<table class="filter-table playlist-available-list">
|
||||
<tr ng-repeat="playlistItem in ctrl.playlistItems">
|
||||
<td ng-if="playlistItem.type === 'dashboard_by_id'">
|
||||
<i class="icon-gf icon-gf-dashboard"></i> {{playlistItem.title}}
|
||||
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
|
||||
<td>
|
||||
<i class="icon-gf icon-gf-dashboard"></i>
|
||||
{{playlistItem.title}}
|
||||
<i class="fa fa-star" ng-show="playlistItem.isStarred"></i>
|
||||
</td>
|
||||
<td ng-if="playlistItem.type === 'dashboard_by_tag'">
|
||||
<a class="search-result-tag label label-tag" tag-color-from-name="playlistItem.title">
|
||||
<td class="add-dashboard">
|
||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addPlaylistItem(playlistItem)">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add to playlist
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="playlist-search-results-container" ng-if="ctrl.filteredTags.length > 0;">
|
||||
<table class="filter-table playlist-available-list">
|
||||
<tr ng-repeat="tag in ctrl.filteredTags">
|
||||
<td>
|
||||
<a class="search-result-tag label label-tag" tag-color-from-name="tag.term">
|
||||
<i class="fa fa-tag"></i>
|
||||
<span>{{playlistItem.title}}</span>
|
||||
<span>{{tag.term}} ({{tag.count}})</span>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td class="selected-playlistitem-settings">
|
||||
<button class="btn btn-inverse btn-mini" ng-hide="$first" ng-click="ctrl.movePlaylistItemUp(playlistItem)">
|
||||
<i class="fa fa-arrow-up"></i>
|
||||
</button>
|
||||
<button class="btn btn-inverse btn-mini" ng-hide="$last" ng-click="ctrl.movePlaylistItemDown(playlistItem)">
|
||||
<i class="fa fa-arrow-down"></i>
|
||||
</button>
|
||||
<button class="btn btn-inverse btn-mini" ng-click="ctrl.removePlaylistItem(playlistItem)">
|
||||
<i class="fa fa-remove"></i>
|
||||
<td class="add-dashboard">
|
||||
<button class="btn btn-inverse btn-mini pull-right" ng-click="ctrl.addTagPlaylistItem(tag)">
|
||||
<i class="fa fa-plus"></i>
|
||||
Add to playlist
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@ -103,12 +95,8 @@
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div class="gf-form-button-row">
|
||||
<a class="btn btn-success " ng-show="ctrl.isNew"
|
||||
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
|
||||
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create new playlist</a>
|
||||
<a class="btn btn-success" ng-show="!ctrl.isNew()"
|
||||
ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()"
|
||||
ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
|
||||
<a class="btn btn-success" ng-show="ctrl.isNew" ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()" ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Create</a>
|
||||
<a class="btn btn-success" ng-show="!ctrl.isNew" ng-disabled="ctrl.playlistEditForm.$invalid || ctrl.isPlaylistEmpty()" ng-click="ctrl.savePlaylist(ctrl.playlist, ctrl.playlistItems)">Save</a>
|
||||
<a class="btn-text" ng-click="ctrl.backToList()">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -10,38 +10,42 @@
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<table class="filter-table">
|
||||
<table class="filter-table filter-table--hover">
|
||||
<thead>
|
||||
<th>
|
||||
<strong>Name</strong>
|
||||
</th>
|
||||
<th>
|
||||
<strong>Start url</strong>
|
||||
</th>
|
||||
<th><strong>Name</strong></th>
|
||||
<th style="width: 100px"></th>
|
||||
<th style="width: 78px"></th>
|
||||
<th style="width: 78px"></th>
|
||||
<th style="width: 25px"></th>
|
||||
</thead>
|
||||
<tr ng-repeat="playlist in ctrl.playlists">
|
||||
<td>
|
||||
<td class="link-td">
|
||||
<a href="playlists/edit/{{playlist.id}}">{{playlist.name}}</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="playlists/play/{{playlist.id}}">playlists/play/{{playlist.id}}</a>
|
||||
<td class="dropdown">
|
||||
<button class="btn btn-inverse btn-small" data-toggle="dropdown">
|
||||
Start playlist
|
||||
<i class="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a href="{{playlist.startUrl}}">
|
||||
<i class="fa fa-play"></i> In Normal mode</span>
|
||||
</a>
|
||||
<a href="{{playlist.startUrl}}?kiosk=tv">
|
||||
<i class="fa fa-play"></i> In TV mode</span>
|
||||
</a>
|
||||
<a href="{{playlist.startUrl}}?kiosk=tv&autofitpanels">
|
||||
<i class="fa fa-play"></i> In TV mode <span class="muted">(with auto fit panels)</span>
|
||||
</a>
|
||||
<a href="{{playlist.startUrl}}?kiosk">
|
||||
<i class="fa fa-play"></i> In Kiosk mode</span>
|
||||
</a>
|
||||
<a ng-href="{{playlist.startUrl}}?kiosk&autofitpanels">
|
||||
<i class="fa fa-play"></i> In Kiosk mode <span class="muted">(with auto fit panels)</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<a href="playlists/play/{{playlist.id}}" class="btn btn-inverse btn-small">
|
||||
<i class="fa fa-play"></i>
|
||||
Play
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<a href="playlists/edit/{{playlist.id}}" class="btn btn-inverse btn-small">
|
||||
<i class="fa fa-edit"></i>
|
||||
Edit
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<td class="text-right">
|
||||
<a ng-click="ctrl.removePlaylist(playlist)" class="btn btn-danger btn-small">
|
||||
<i class="fa fa-remove"></i>
|
||||
</a>
|
||||
@ -49,18 +53,16 @@
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div ng-if="ctrl.playlists.length === 0">
|
||||
<empty-list-cta model="{
|
||||
title: 'There are no playlists created yet',
|
||||
buttonIcon: 'fa fa-plus',
|
||||
buttonLink: 'playlists/create',
|
||||
buttonTitle: ' Create Playlist',
|
||||
proTip: 'You can run the playlist in Kiosk Mode.',
|
||||
proTip: 'You can use playlists to remove control TVs',
|
||||
proTipLink: 'http://docs.grafana.org/reference/playlist/',
|
||||
proTipLinkTitle: 'Learn more',
|
||||
proTipTarget: '_blank'
|
||||
}" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -19,29 +19,18 @@ export class PlaylistEditCtrl {
|
||||
/** @ngInject */
|
||||
constructor(private $scope, private backendSrv, private $location, $route, navModelSrv) {
|
||||
this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
|
||||
this.isNew = $route.current.params.id;
|
||||
this.isNew = !$route.current.params.id;
|
||||
|
||||
if ($route.current.params.id) {
|
||||
const playlistId = $route.current.params.id;
|
||||
|
||||
backendSrv.get('/api/playlists/' + playlistId).then(result => {
|
||||
this.playlist = result;
|
||||
this.navModel.node = {
|
||||
text: result.name,
|
||||
icon: this.navModel.node.icon,
|
||||
};
|
||||
this.navModel.breadcrumbs.push(this.navModel.node);
|
||||
});
|
||||
|
||||
backendSrv.get('/api/playlists/' + playlistId + '/items').then(result => {
|
||||
this.playlistItems = result;
|
||||
});
|
||||
} else {
|
||||
this.navModel.node = {
|
||||
text: 'New playlist',
|
||||
icon: this.navModel.node.icon,
|
||||
};
|
||||
this.navModel.breadcrumbs.push(this.navModel.node);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,7 @@ function grafanaRoutes($routeProvider) {
|
||||
controller: 'PlaylistEditCtrl',
|
||||
})
|
||||
.when('/playlists/play/:id', {
|
||||
templateUrl: 'public/app/features/playlist/partials/playlists.html',
|
||||
controllerAs: 'ctrl',
|
||||
controller: 'PlaylistsCtrl',
|
||||
template: '',
|
||||
resolve: {
|
||||
init: function(playlistSrv, $route) {
|
||||
const playlistId = $route.current.params.id;
|
||||
|
@ -8,7 +8,7 @@ export class PlaylistSearchCtrl {
|
||||
|
||||
/** @ngInject */
|
||||
constructor($timeout, private backendSrv) {
|
||||
this.query = { query: '', tag: [], starred: false, limit: 30 };
|
||||
this.query = { query: '', tag: [], starred: false, limit: 20 };
|
||||
|
||||
$timeout(() => {
|
||||
this.query.query = '';
|
||||
|
@ -1,6 +1,8 @@
|
||||
import coreModule from '../../core/core_module';
|
||||
import kbn from 'app/core/utils/kbn';
|
||||
import appEvents from 'app/core/app_events';
|
||||
import _ from 'lodash';
|
||||
import { toUrlParams } from 'app/core/utils/url';
|
||||
|
||||
class PlaylistSrv {
|
||||
private cancelPromise: any;
|
||||
@ -11,42 +13,27 @@ class PlaylistSrv {
|
||||
public isPlaying: boolean;
|
||||
|
||||
/** @ngInject */
|
||||
constructor(private $location: any, private $timeout: any, private backendSrv: any, private $routeParams: any) {}
|
||||
constructor(private $location: any, private $timeout: any, private backendSrv: any) {}
|
||||
|
||||
next() {
|
||||
this.$timeout.cancel(this.cancelPromise);
|
||||
|
||||
const playedAllDashboards = this.index > this.dashboards.length - 1;
|
||||
|
||||
if (playedAllDashboards) {
|
||||
window.location.href = this.getUrlWithKioskMode();
|
||||
window.location.href = this.startUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
const dash = this.dashboards[this.index];
|
||||
this.$location.url('dashboard/' + dash.uri);
|
||||
const queryParams = this.$location.search();
|
||||
const filteredParams = _.pickBy(queryParams, value => value !== null);
|
||||
|
||||
this.$location.url('dashboard/' + dash.uri + '?' + toUrlParams(filteredParams));
|
||||
|
||||
this.index++;
|
||||
this.cancelPromise = this.$timeout(() => this.next(), this.interval);
|
||||
}
|
||||
|
||||
getUrlWithKioskMode() {
|
||||
const inKioskMode = document.body.classList.contains('page-kiosk-mode');
|
||||
|
||||
// check if should add kiosk query param
|
||||
if (inKioskMode && this.startUrl.indexOf('kiosk') === -1) {
|
||||
return this.startUrl + '?kiosk=true';
|
||||
}
|
||||
|
||||
// check if should remove kiosk query param
|
||||
if (!inKioskMode) {
|
||||
return this.startUrl.split('?')[0];
|
||||
}
|
||||
|
||||
// already has kiosk query param, just return startUrl
|
||||
return this.startUrl;
|
||||
}
|
||||
|
||||
prev() {
|
||||
this.index = Math.max(this.index - 2, 0);
|
||||
this.next();
|
||||
@ -59,10 +46,6 @@ class PlaylistSrv {
|
||||
this.index = 0;
|
||||
this.isPlaying = true;
|
||||
|
||||
if (this.$routeParams.kiosk) {
|
||||
appEvents.emit('toggle-kiosk-mode');
|
||||
}
|
||||
|
||||
this.backendSrv.get(`/api/playlists/${playlistId}`).then(playlist => {
|
||||
this.backendSrv.get(`/api/playlists/${playlistId}/dashboards`).then(dashboards => {
|
||||
this.dashboards = dashboards;
|
||||
@ -73,6 +56,13 @@ class PlaylistSrv {
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.isPlaying) {
|
||||
const queryParams = this.$location.search();
|
||||
if (queryParams.kiosk) {
|
||||
appEvents.emit('toggle-kiosk-mode', { exit: true });
|
||||
}
|
||||
}
|
||||
|
||||
this.index = 0;
|
||||
this.isPlaying = false;
|
||||
|
||||
|
@ -10,7 +10,10 @@ export class PlaylistsCtrl {
|
||||
this.navModel = navModelSrv.getNav('dashboards', 'playlists', 0);
|
||||
|
||||
backendSrv.get('/api/playlists').then(result => {
|
||||
this.playlists = result;
|
||||
this.playlists = result.map(item => {
|
||||
item.startUrl = `playlists/play/${item.id}`;
|
||||
return item;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,8 @@ export class LoadDashboardCtrl {
|
||||
}
|
||||
}
|
||||
|
||||
if ($routeParams.autofitpanels) {
|
||||
result.meta.autofitpanels = true;
|
||||
}
|
||||
result.meta.autofitpanels = $routeParams.autofitpanels;
|
||||
result.meta.kiosk = $routeParams.kiosk;
|
||||
|
||||
$scope.initDashboard(result, $scope);
|
||||
});
|
||||
|
@ -23,6 +23,7 @@
|
||||
@include navbar-alt-look();
|
||||
}
|
||||
|
||||
.navbar-buttons--tv,
|
||||
.navbar-button--add-panel,
|
||||
.navbar-button--star,
|
||||
.navbar-button--save,
|
||||
@ -45,6 +46,7 @@
|
||||
|
||||
.navbar-button--add-panel,
|
||||
.navbar-button--star,
|
||||
.navbar-button--tv,
|
||||
.navbar-page-btn .fa-caret-down {
|
||||
display: none;
|
||||
}
|
||||
@ -106,6 +108,10 @@
|
||||
display: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&--zoom {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar__spacer {
|
||||
@ -119,6 +125,7 @@
|
||||
font-weight: $btn-font-weight;
|
||||
padding: 6px 11px;
|
||||
line-height: 16px;
|
||||
height: 30px;
|
||||
color: $text-muted;
|
||||
border: 1px solid $navbar-button-border;
|
||||
margin-right: 3px;
|
||||
@ -133,7 +140,7 @@
|
||||
}
|
||||
|
||||
&--add-panel {
|
||||
padding: 3px 10px;
|
||||
padding: 2px 10px;
|
||||
|
||||
.gicon {
|
||||
font-size: 22px;
|
||||
@ -146,7 +153,7 @@
|
||||
.fa {
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,41 @@
|
||||
.page-kiosk-mode {
|
||||
.view-mode--inactive {
|
||||
.react-resizable-handle,
|
||||
.add-row-panel-hint,
|
||||
.dash-row-menu-container,
|
||||
.navbar-buttons--actions,
|
||||
.panel-info-corner--info,
|
||||
.panel-info-corner--links {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-page-btn {
|
||||
transform: translate3d(-36px, 0, 0);
|
||||
i {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-button--zoom,
|
||||
.navbar-button--refresh {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.view-mode--playlist {
|
||||
@extend .view-mode--inactive;
|
||||
}
|
||||
|
||||
.view-mode--tv {
|
||||
@extend .view-mode--inactive;
|
||||
|
||||
.submenu-controls {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.view-mode--kiosk {
|
||||
@extend .view-mode--tv;
|
||||
|
||||
.sidemenu,
|
||||
.navbar {
|
||||
display: none;
|
||||
@ -6,37 +43,7 @@
|
||||
.scroll-canvas--dashboard {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.playlist-active,
|
||||
.user-activity-low {
|
||||
.react-resizable-handle,
|
||||
.add-row-panel-hint,
|
||||
.dash-row-menu-container,
|
||||
.navbar-button--refresh,
|
||||
.navbar-buttons--zoom,
|
||||
.navbar-buttons--actions,
|
||||
.panel-menu-container,
|
||||
.panel-info-corner--info,
|
||||
.panel-info-corner--links {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.navbar-page-btn {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
transform: translate3d(-40px, 0, 0);
|
||||
i {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.gf-timepicker-nav-btn {
|
||||
transform: translate3d(40px, 0, 0);
|
||||
.submenu-controls {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user