mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* ux: Make new tabs responsive #10082 * ux: Add possibility to manipulate url in angular router outside of angular - and use it in the responsive navigation #10082
This commit is contained in:
committed by
Torkel Ödegaard
parent
8adaf99bff
commit
e8807f4bce
@@ -1,12 +1,10 @@
|
|||||||
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
import { react2AngularDirective } from 'app/core/utils/react2angular';
|
||||||
import { PasswordStrength } from './components/PasswordStrength';
|
import { PasswordStrength } from './components/PasswordStrength';
|
||||||
import PageHeader from './components/PageHeader';
|
import PageHeader from './components/PageHeader/PageHeader';
|
||||||
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
|
import EmptyListCTA from './components/EmptyListCTA/EmptyListCTA';
|
||||||
|
|
||||||
export function registerAngularDirectives() {
|
export function registerAngularDirectives() {
|
||||||
|
|
||||||
react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
|
react2AngularDirective('passwordStrength', PasswordStrength, ['password']);
|
||||||
react2AngularDirective('pageHeader', PageHeader, ['model', "noTabs"]);
|
react2AngularDirective('pageHeader', PageHeader, ['model', 'noTabs']);
|
||||||
react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
|
react2AngularDirective('emptyListCta', EmptyListCTA, ['model']);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { NavModel, NavModelItem } from '../nav_model_srv';
|
import { NavModel, NavModelItem } from '../../nav_model_srv';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
export interface IProps {
|
export interface IProps {
|
||||||
model: NavModel;
|
model: NavModel;
|
||||||
@@ -26,8 +27,44 @@ function TabItem(tab: NavModelItem) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Tabs({main}: {main: NavModelItem}) {
|
function SelectOption(navItem: NavModelItem) {
|
||||||
return <ul className="gf-tabs">{main.children.map(TabItem)}</ul>;
|
if (navItem.hideFromTabs) { // TODO: Rename hideFromTabs => hideFromNav
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<option key={navItem.url} value={navItem.url}>
|
||||||
|
{navItem.text}
|
||||||
|
</option>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Navigation({main}: {main: NavModelItem}) {
|
||||||
|
return (<nav>
|
||||||
|
<SelectNav customCss="page-header__select_nav" main={main} />
|
||||||
|
<Tabs customCss="page-header__tabs" main={main} />
|
||||||
|
</nav>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectNav({main, customCss}: {main: NavModelItem, customCss: string}) {
|
||||||
|
const defaultSelectedItem = main.children.find(navItem => {
|
||||||
|
return navItem.active === true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const gotoUrl = evt => {
|
||||||
|
var element = evt.target;
|
||||||
|
var url = element.options[element.selectedIndex].value;
|
||||||
|
appEvents.emit('location-change', {href: url});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (<select
|
||||||
|
className={`gf-select-nav ${customCss}`}
|
||||||
|
defaultValue={defaultSelectedItem.url}
|
||||||
|
onChange={gotoUrl}>{main.children.map(SelectOption)}</select>);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tabs({main, customCss}: {main: NavModelItem, customCss: string}) {
|
||||||
|
return <ul className={`gf-tabs ${customCss}`}>{main.children.map(TabItem)}</ul>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PageHeader extends React.Component<IProps, any> {
|
export default class PageHeader extends React.Component<IProps, any> {
|
||||||
@@ -63,7 +100,7 @@ export default class PageHeader extends React.Component<IProps, any> {
|
|||||||
<div className="page-container">
|
<div className="page-container">
|
||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
{this.renderHeaderTitle(this.props.model.main)}
|
{this.renderHeaderTitle(this.props.model.main)}
|
||||||
{this.props.model.main.children && <Tabs main={this.props.model.main} />}
|
{this.props.model.main.children && <Navigation main={this.props.model.main} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -12,7 +12,7 @@ import Drop from 'tether-drop';
|
|||||||
export class GrafanaCtrl {
|
export class GrafanaCtrl {
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv) {
|
constructor($scope, alertSrv, utilSrv, $rootScope, $controller, contextSrv, globalEventSrv) {
|
||||||
|
|
||||||
$scope.init = function() {
|
$scope.init = function() {
|
||||||
$scope.contextSrv = contextSrv;
|
$scope.contextSrv = contextSrv;
|
||||||
@@ -23,6 +23,7 @@ export class GrafanaCtrl {
|
|||||||
profiler.init(config, $rootScope);
|
profiler.init(config, $rootScope);
|
||||||
alertSrv.init();
|
alertSrv.init();
|
||||||
utilSrv.init();
|
utilSrv.init();
|
||||||
|
globalEventSrv.init();
|
||||||
|
|
||||||
$scope.dashAlerts = alertSrv;
|
$scope.dashAlerts = alertSrv;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,5 +8,6 @@ define([
|
|||||||
'./segment_srv',
|
'./segment_srv',
|
||||||
'./backend_srv',
|
'./backend_srv',
|
||||||
'./dynamic_directive_srv',
|
'./dynamic_directive_srv',
|
||||||
|
'./global_event_srv'
|
||||||
],
|
],
|
||||||
function () {});
|
function () {});
|
||||||
|
|||||||
21
public/app/core/services/global_event_srv.ts
Normal file
21
public/app/core/services/global_event_srv.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import coreModule from 'app/core/core_module';
|
||||||
|
import appEvents from 'app/core/app_events';
|
||||||
|
|
||||||
|
// This service is for registering global events.
|
||||||
|
// Good for communication react > angular and vice verse
|
||||||
|
export class GlobalEventSrv {
|
||||||
|
|
||||||
|
/** @ngInject */
|
||||||
|
constructor(private $location, private $timeout) {
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
appEvents.on('location-change', payload => {
|
||||||
|
this.$timeout(() => { // A hack to use timeout when we're changing things (in this case the url) from outside of Angular.
|
||||||
|
this.$location.path(payload.href);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coreModule.service('globalEventSrv', GlobalEventSrv);
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
export function react2AngularDirective(name: string, component: any, options: any) {
|
export function react2AngularDirective(name: string, component: any, options: any) {
|
||||||
|
|
||||||
coreModule.directive(name, ['reactDirective', reactDirective => {
|
coreModule.directive(name, ['reactDirective', reactDirective => {
|
||||||
return reactDirective(component, options);
|
return reactDirective(component, options);
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
///<reference path="../../headers/common.d.ts" />
|
///<reference path="../../headers/common.d.ts" />
|
||||||
|
|
||||||
import coreModule from '../../core/core_module';
|
import coreModule from '../../core/core_module';
|
||||||
|
import {appEvents} from 'app/core/core';
|
||||||
|
|
||||||
export class DataSourcesCtrl {
|
export class DataSourcesCtrl {
|
||||||
datasources: any;
|
datasources: any;
|
||||||
@@ -11,13 +12,24 @@ export class DataSourcesCtrl {
|
|||||||
private $scope,
|
private $scope,
|
||||||
private backendSrv,
|
private backendSrv,
|
||||||
private datasourceSrv,
|
private datasourceSrv,
|
||||||
|
private $location,
|
||||||
private navModelSrv) {
|
private navModelSrv) {
|
||||||
|
|
||||||
this.navModel = this.navModelSrv.getNav('cfg', 'datasources', 0);
|
this.navModel = this.navModelSrv.getNav('cfg', 'datasources', 0);
|
||||||
|
this.navigateToUrl = this.navigateToUrl.bind(this);
|
||||||
backendSrv.get('/api/datasources').then(result => {
|
backendSrv.get('/api/datasources').then(result => {
|
||||||
this.datasources = result;
|
this.datasources = result;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
appEvents.on('location-change', payload => {
|
||||||
|
this.navigateToUrl(payload.href);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToUrl(url) {
|
||||||
|
// debugger;
|
||||||
|
this.$location.path(url);
|
||||||
|
this.$location.replace();
|
||||||
}
|
}
|
||||||
|
|
||||||
removeDataSourceConfirmed(ds) {
|
removeDataSourceConfirmed(ds) {
|
||||||
|
|||||||
@@ -235,5 +235,5 @@ $dashboard-padding: $panel-margin * 2;
|
|||||||
$panel-padding: 0px 10px 5px 10px;
|
$panel-padding: 0px 10px 5px 10px;
|
||||||
|
|
||||||
// tabs
|
// tabs
|
||||||
$tabs-padding: 9px 15px 9px;
|
$tabs-padding: 10px 15px 9px;
|
||||||
|
|
||||||
|
|||||||
@@ -72,6 +72,21 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.page-header__select_nav {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header__tabs {
|
||||||
|
display: none;
|
||||||
|
@include media-breakpoint-up(lg) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.page-breadcrumbs {
|
.page-breadcrumbs {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: block;
|
display: block;
|
||||||
border: solid transparent;
|
border: solid transparent;
|
||||||
border-width: 2px 1px 1px;
|
border-width: 0 1px 1px;
|
||||||
border-radius: 3px 3px 0 0;
|
border-radius: 3px 3px 0 0;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
@@ -34,6 +34,18 @@
|
|||||||
border-color: $orange $tab-border-color transparent;
|
border-color: $orange $tab-border-color transparent;
|
||||||
background: $page-bg;
|
background: $page-bg;
|
||||||
color: $link-color;
|
color: $link-color;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: block;
|
||||||
|
content: ' ';
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
top: 0;
|
||||||
|
background-image: linear-gradient(to right, #ffd500 0%, #ff4400 99%, #ff4400 100%);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user