Переглянути джерело

Dashboard settings starting to work

Torkel Ödegaard 6 роки тому
батько
коміт
8dec74689d

+ 2 - 1
public/app/core/app_events.ts

@@ -1,4 +1,5 @@
 import { Emitter } from './utils/emitter';
 
-const appEvents = new Emitter();
+export const appEvents = new Emitter();
+
 export default appEvents;

+ 144 - 0
public/app/features/dashboard/components/DashNav/DashNav.tsx

@@ -0,0 +1,144 @@
+// Libaries
+import React, { PureComponent } from 'react';
+import { connect } from 'react-redux';
+
+// Utils & Services
+import { appEvents } from 'app/core/app_events';
+
+// State
+import { updateLocation } from 'app/core/actions';
+
+// Types
+import { DashboardModel } from '../../state/DashboardModel';
+
+export interface Props {
+  dashboard: DashboardModel | null;
+  updateLocation: typeof updateLocation;
+}
+
+export class DashNav extends PureComponent<Props> {
+  onOpenSearch = () => {
+    appEvents.emit('show-dash-search');
+  };
+
+  onAddPanel = () => {};
+  onOpenSettings = () => {
+    this.props.updateLocation({
+      query: {
+        editview: 'settings',
+      },
+      partial: true,
+    })
+  };
+
+  renderLoadingState() {
+    return (
+      <div className="navbar">
+        <div>
+          <a className="navbar-page-btn" onClick={this.onOpenSearch}>
+            <i className="gicon gicon-dashboard" />
+            Loading...
+            <i className="fa fa-caret-down" />
+          </a>
+        </div>
+      </div>
+    );
+  }
+
+  render() {
+    let { dashboard } = this.props;
+
+    if (!dashboard) {
+      return this.renderLoadingState();
+    }
+
+    const haveFolder = dashboard.meta.folderId > 0;
+    const { canEdit, canSave, folderTitle, showSettings } = dashboard.meta;
+
+    return (
+      <div className="navbar">
+        <div>
+          <a className="navbar-page-btn" onClick={this.onOpenSearch}>
+            <i className="gicon gicon-dashboard" />
+            {haveFolder && <span className="navbar-page-btn--folder">{folderTitle} / </span>}
+            {dashboard.title}
+            <i className="fa fa-caret-down" />
+          </a>
+        </div>
+
+        <div className="navbar__spacer" />
+        {/*
+        <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 className="navbar-buttons navbar-buttons--actions">
+          {canEdit && (
+            <button className="btn navbar-button navbar-button--add-panel" title="Add panel" onClick={this.onAddPanel}>
+              <i className="gicon gicon-add-panel" />
+            </button>
+          )}
+
+          {showSettings && (
+            <button
+              className="btn navbar-button navbar-button--settings"
+              onClick={this.onOpenSettings}
+              title="Dashboard Settings"
+            >
+              <i className="fa fa-cog" />
+            </button>
+          )}
+
+          {
+            // 	<button class="btn navbar-button navbar-button--star" ng-show="::ctrl.dashboard.meta.canStar" ng-click="ctrl.starDashboard()" bs-tooltip="'Mark as favorite'" data-placement="bottom">
+              // 		<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">
+              // 		<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">
+              // 		<i class="fa fa-save"></i>
+              // 	</button>
+            //
+            // 	<a class="btn navbar-button navbar-button--snapshot-origin" ng-if="::ctrl.dashboard.snapshot.originalUrl" href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom">
+              // 		<i class="fa fa-link"></i>
+              // 	</a>
+            //
+            // 	<button class="btn navbar-button navbar-button--settings" ng-click="ctrl.toggleSettings()" bs-tooltip="'Dashboard Settings'" data-placement="bottom" ng-show="ctrl.dashboard.meta.showSettings">
+              // 		<i class="fa fa-cog"></i>
+              // 	</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">
+            // 	<button class="btn navbar-button navbar-button--primary" ng-click="ctrl.close()" bs-tooltip="'Back to dashboard'" data-placement="bottom">
+            // 		<i class="fa fa-reply"></i>
+            // 	</button>
+            // </div>
+          }
+        </div>
+      </div>
+    );
+  }
+}
+
+const mapStateToProps = () => ({
+});
+
+const mapDispatchToProps = {
+  updateLocation
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(DashNav);

+ 2 - 0
public/app/features/dashboard/components/DashNav/index.ts

@@ -1 +1,3 @@
 export { DashNavCtrl } from './DashNavCtrl';
+import DashNav from './DashNav';
+export { DashNav };

+ 36 - 0
public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx

@@ -0,0 +1,36 @@
+// Libaries
+import React, { PureComponent } from 'react';
+
+// Utils & Services
+import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
+
+// Types
+import { DashboardModel } from '../../state/DashboardModel';
+
+export interface Props {
+  dashboard: DashboardModel | null;
+}
+
+export class DashboardSettings extends PureComponent<Props> {
+  element: HTMLElement;
+  angularCmp: AngularComponent;
+
+  componentDidMount() {
+    const loader = getAngularLoader();
+
+    const template = '<dashboard-settings dashboard="dashboard" class="dashboard-settings" />';
+    const scopeProps = { dashboard: this.props.dashboard };
+
+    this.angularCmp = loader.load(this.element, scopeProps, template);
+  }
+
+  componentWillUnmount() {
+    if (this.angularCmp) {
+      this.angularCmp.destroy();
+    }
+  }
+
+  render() {
+    return <div className="panel-height-helper" ref={element => this.element = element} />;
+  }
+}

+ 1 - 0
public/app/features/dashboard/components/DashboardSettings/index.ts

@@ -1 +1,2 @@
 export { SettingsCtrl } from './SettingsCtrl';
+export { DashboardSettings } from './DashboardSettings';

+ 86 - 14
public/app/features/dashboard/containers/DashboardPage.tsx

@@ -1,14 +1,19 @@
 // Libraries
-import React, { Component } from 'react';
+import $ from 'jquery';
+import React, { PureComponent } from 'react';
 import { hot } from 'react-hot-loader';
 import { connect } from 'react-redux';
+import classNames from 'classnames';
 
 // Components
 import { LoadingPlaceholder } from '@grafana/ui';
 import { DashboardGrid } from '../dashgrid/DashboardGrid';
+import { DashNav } from '../components/DashNav';
+import { DashboardSettings } from '../components/DashboardSettings';
 
 // Redux
 import { initDashboard } from '../state/initDashboard';
+import { setDashboardModel } from '../state/actions';
 
 // Types
 import { StoreState } from 'app/types';
@@ -20,22 +25,23 @@ interface Props {
   urlUid?: string;
   urlSlug?: string;
   urlType?: string;
+  editview: string;
   $scope: any;
   $injector: any;
   initDashboard: typeof initDashboard;
+  setDashboardModel: typeof setDashboardModel;
   loadingState: DashboardLoadingState;
   dashboard: DashboardModel;
 }
 
 interface State {
-  dashboard: DashboardModel | null;
-  notFound: boolean;
+  isSettingsOpening: boolean;
 }
 
-export class DashboardPage extends Component<Props, State> {
+export class DashboardPage extends PureComponent<Props, State> {
   state: State = {
-    dashboard: null,
-    notFound: false,
+    isSettingsOpening: false,
+    isSettingsOpen: false,
   };
 
   async componentDidMount() {
@@ -45,18 +51,82 @@ export class DashboardPage extends Component<Props, State> {
       urlSlug: this.props.urlSlug,
       urlUid: this.props.urlUid,
       urlType: this.props.urlType,
-    })
+    });
   }
 
-  render() {
-    const { loadingState, dashboard } = this.props;
+  componentDidUpdate(prevProps: Props) {
+    const { dashboard, editview } = this.props;
+
+    // when dashboard has loaded subscribe to somme events
+    if (prevProps.dashboard === null && dashboard) {
+      dashboard.events.on('view-mode-changed', this.onViewModeChanged);
+
+      // set initial fullscreen class state
+      this.setPanelFullscreenClass();
+    }
+
+    if (!prevProps.editview && editview) {
+      this.setState({ isSettingsOpening: true });
+      setTimeout(() => {
+        this.setState({ isSettingsOpening: false});
+      }, 10);
+    }
+  }
+
+  onViewModeChanged = () => {
+    this.setPanelFullscreenClass();
+  };
+
+  setPanelFullscreenClass() {
+    $('body').toggleClass('panel-in-fullscreen', this.props.dashboard.meta.fullscreen === true);
+  }
 
-    if (!dashboard) {
-      return <LoadingPlaceholder text={loadingState.toString()} />;
+  componentWillUnmount() {
+    if (this.props.dashboard) {
+      this.props.dashboard.destroy();
+      this.props.setDashboardModel(null);
     }
+  }
+
+  renderLoadingState() {
+    return <LoadingPlaceholder text="Loading" />;
+  }
+
+  renderDashboard() {
+    const { dashboard, editview } = this.props;
+
+    const classes = classNames({
+      'dashboard-container': true,
+      'dashboard-container--has-submenu': dashboard.meta.submenuEnabled
+    });
+
+    return (
+      <div className="scroll-canvas scroll-canvas--dashboard">
+        {dashboard && editview && <DashboardSettings dashboard={dashboard} />}
+
+        <div className={classes}>
+          <DashboardGrid dashboard={dashboard} />
+        </div>
+      </div>
+    );
+  }
+
+  render() {
+    const { dashboard, editview } = this.props;
+    const { isSettingsOpening } = this.state;
+
+    const classes = classNames({
+      'dashboard-page--settings-opening': isSettingsOpening,
+      'dashboard-page--settings-open': !isSettingsOpening && editview,
+    });
 
-    console.log(dashboard);
-    return <DashboardGrid dashboard={dashboard} />
+    return (
+      <div className={classes}>
+        <DashNav dashboard={dashboard} />
+        {!dashboard && this.renderLoadingState()}
+        {dashboard && this.renderDashboard()}
+      </div>
+    );
   }
 }
 
@@ -65,12 +135,14 @@ const mapStateToProps = (state: StoreState) => ({
   urlSlug: state.location.routeParams.slug,
   urlType: state.location.routeParams.type,
   panelId: state.location.query.panelId,
+  editview: state.location.query.editview,
   loadingState: state.dashboard.loadingState,
   dashboard: state.dashboard.model as DashboardModel,
 });
 
 const mapDispatchToProps = {
-  initDashboard
+  initDashboard,
+  setDashboardModel
 };
 
 export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));

+ 1 - 0
public/app/features/dashboard/state/initDashboard.ts

@@ -67,6 +67,7 @@ export function initDashboard({ injector, scope, urlUid, urlSlug, urlType }: Ini
       dashboard.updateSubmenuVisibility();
       dashboard.autoFitPanels(window.innerHeight);
 
+      // init unsaved changes tracking
       injector.get('unsavedChangesSrv').init(dashboard, scope);
 
       scope.dashboard = dashboard;

+ 1 - 1
public/app/features/dashboard/state/reducers.ts

@@ -31,7 +31,7 @@ export const dashboardReducer = reducerFactory(initialState)
       model: action.payload
     }),
   })
-  .create()
+  .create();
 
 export default {
   dashboard: dashboardReducer,

+ 3 - 0
public/sass/components/_dashboard_settings.scss

@@ -16,6 +16,9 @@
     opacity: 1;
     transition: opacity 300ms ease-in-out;
   }
+  .dashboard-container {
+    display: none;
+  }
 }
 
 .dashboard-settings__content {