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:
Torkel Ödegaard 2018-08-30 11:52:31 +02:00 committed by GitHub
parent 36bc8b77fb
commit 154fbe2413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 279 additions and 286 deletions

View File

@ -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);
}
}

View File

@ -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();
});

View File

@ -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 });
}
}

View File

@ -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));

View File

@ -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">

View File

@ -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) {

View File

@ -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">&nbsp; 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>

View File

@ -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) {

View File

@ -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>&nbsp;&nbsp;{{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>
&nbsp;&nbsp;{{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}} &nbsp;({{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>&nbsp;&nbsp;{{playlistItem.title}}
<tr ng-repeat="playlistItem in ctrl.filteredDashboards">
<td>
<i class="icon-gf icon-gf-dashboard"></i>
&nbsp;&nbsp;{{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}} &nbsp;({{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>

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 = '';

View File

@ -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;

View File

@ -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;
});
});
}

View File

@ -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);
});

View File

@ -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;
}
}

View File

@ -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;
}
}