Browse Source

wip: progress on edit mode ux with tabs

Torkel Ödegaard 7 years ago
parent
commit
a7daf58bb9

+ 1 - 1
public/app/core/components/CustomScrollbar/CustomScrollbar.tsx

@@ -15,7 +15,7 @@ interface Props {
 class CustomScrollbar extends PureComponent<Props> {
   static defaultProps: Partial<Props> = {
     customClassName: 'custom-scrollbars',
-    autoHide: true,
+    autoHide: false,
     autoHideTimeout: 200,
     autoHideDuration: 200,
     hideTracksWhenNotNeeded: false,

+ 84 - 0
public/app/features/dashboard/dashgrid/EditorTabBody.tsx

@@ -0,0 +1,84 @@
+import React, { PureComponent } from 'react';
+import CustomScrollbar from 'app/core/components/CustomScrollbar/CustomScrollbar';
+import { FadeIn } from 'app/core/components/Animations/FadeIn';
+
+interface Props {
+  selectedText?: string;
+  selectedImage?: string;
+  children: JSX.Element;
+  toolbarItems: EditorToolBarView[];
+}
+
+export interface EditorToolBarView {
+  title: string;
+  imgSrc: string;
+  render: () => JSX.Element;
+}
+
+interface State {
+  openView?: EditorToolBarView;
+}
+
+export class EditorTabBody extends PureComponent<Props, State> {
+  constructor(props) {
+    super(props);
+
+    this.state = {
+      openView: null,
+    };
+  }
+
+  onToggleToolBarView = (item: EditorToolBarView) => {
+    this.setState({
+      openView: item === this.state.openView ? null : item,
+    });
+  }
+
+  onCloseOpenView = () => {
+    this.setState({ openView: null });
+  }
+
+  renderToolBarViewToggle(item: EditorToolBarView) {
+    return (
+      <div className="edit-section__selected" onClick={() => this.onToggleToolBarView(item)} key={item.title}>
+        <img className="edit-section__selected-image" src={item.imgSrc} />
+        <div className="edit-section__selected-name">{item.title}</div>
+        <i className="fa fa-caret-down" />
+      </div>
+    );
+  }
+
+  renderOpenView(view: EditorToolBarView) {
+    return (
+      <div className="editor-toolbar-view">
+        <button className="editor-toolbar-view__close" onClick={this.onCloseOpenView}>
+          <i className="fa fa-remove" />
+        </button>
+        {view.render()}
+      </div>
+    );
+  }
+
+  render() {
+    const { children, toolbarItems} = this.props;
+    const { openView } = this.state;
+
+    return (
+      <>
+      <div className="edit-section">
+        <div className="edit-section__header">{toolbarItems.map(item => this.renderToolBarViewToggle(item))}</div>
+      </div>
+      <div className="panel-editor__scroll">
+        <CustomScrollbar>
+          <div className="panel-editor__content">
+            <FadeIn in={openView !== null} duration={300}>
+              {openView && this.renderOpenView(openView)}
+            </FadeIn>
+            {children}
+          </div>
+        </CustomScrollbar>
+      </div>
+      </>
+      );
+  }
+}

+ 18 - 128
public/app/features/dashboard/dashgrid/PanelEditor.tsx

@@ -2,8 +2,7 @@ import React, { PureComponent } from 'react';
 import classNames from 'classnames';
 
 import { QueriesTab } from './QueriesTab';
-import { VizTypePicker } from './VizTypePicker';
-import CustomScrollbar from 'app/core/components/CustomScrollbar/CustomScrollbar';
+import { VisualizationTab } from './VisualizationTab';
 
 import { store } from 'app/store/configureStore';
 import { updateLocation } from 'app/core/actions';
@@ -38,35 +37,6 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
     ];
   }
 
-  renderQueriesTab() {
-    return <QueriesTab panel={this.props.panel} dashboard={this.props.dashboard} />;
-  }
-
-  renderPanelOptions() {
-    const { plugin, panel } = this.props;
-    const { PanelOptionsComponent } = plugin.exports;
-
-    if (PanelOptionsComponent) {
-      return <PanelOptionsComponent options={panel.getOptions()} onChange={this.onPanelOptionsChanged} />;
-    } else {
-      return <p>Visualization has no options</p>;
-    }
-  }
-
-  onPanelOptionsChanged = (options: any) => {
-    this.props.panel.updateOptions(options);
-    this.forceUpdate();
-  };
-
-  renderVizTab() {
-    return (
-      <div className="viz-editor">
-        <VizTypePicker current={this.props.plugin} onTypeChanged={this.props.onTypeChanged} />
-        {this.renderPanelOptions()}
-      </div>
-    );
-  }
-
   onChangeTab = (tab: PanelEditorTab) => {
     store.dispatch(
       updateLocation({
@@ -87,50 +57,8 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
   };
 
   render() {
-    return this.renderAsTabs();
-    // return this.renderAsBoxes();
-    // const { location } = store.getState();
-    // const activeTab = location.query.tab || 'queries';
-    //
-    // return (
-    //   <div className="panel-editor-container__editor">
-    //     <div className="panel-editor-resizer">
-    //       <div className="panel-editor-resizer__handle">
-    //         <div className="panel-editor-resizer__handle-dots" />
-    //       </div>
-    //     </div>
-    //     <div className="panel-editor__aside">
-    //       <h2 className="panel-editor__aside-header">
-    //         <i className="fa fa-cog" />
-    //         Edit Panel
-    //       </h2>
-    //
-    //       {this.tabs.map(tab => {
-    //         return <TabItem tab={tab} activeTab={activeTab} onClick={this.onChangeTab} key={tab.id} />;
-    //       })}
-    //
-    //       <div className="panel-editor__aside-actions">
-    //         <button className="btn btn-secondary" onClick={this.onClose}>
-    //           Back to dashboard
-    //         </button>
-    //         <button className="btn btn-inverse" onClick={this.onClose}>
-    //           Discard changes
-    //         </button>
-    //       </div>
-    //     </div>
-    //     <div className="panel-editor__content">
-    //       <CustomScrollbar>
-    //         {activeTab === 'queries' && this.renderQueriesTab()}
-    //         {activeTab === 'visualization' && this.renderVizTab()}
-    //       </CustomScrollbar>
-    //     </div>
-    //   </div>
-    // );
-  }
-
-  renderAsTabs() {
+    const { panel, dashboard, onTypeChanged, plugin } = this.props;
     const { location } = store.getState();
-    const { panel } = this.props;
     const activeTab = location.query.tab || 'queries';
 
     return (
@@ -141,49 +69,22 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
           </div>
         </div>
 
-        <div className="tabbed-view tabbed-view--new">
-          <div className="tabbed-view-header">
-            <h3 className="tabbed-view-panel-title">{panel.title}</h3>
-
-            <ul className="gf-tabs">
-              {this.tabs.map(tab => {
-                return <OldTabItem tab={tab} activeTab={activeTab} onClick={this.onChangeTab} key={tab.id} />;
-              })}
-            </ul>
+        <div className="panel-editor-tabs">
+          <ul className="gf-tabs">
+            {this.tabs.map(tab => {
+              return <TabItem tab={tab} activeTab={activeTab} onClick={this.onChangeTab} key={tab.id} />;
+            })}
+          </ul>
 
-            <button className="tabbed-view-close-btn" onClick={this.onClose}>
-              <i className="fa fa-remove" />
-            </button>
-          </div>
-
-          <div className="tabbed-view-body">
-            <CustomScrollbar>
-              {activeTab === 'queries' && this.renderQueriesTab()}
-              {activeTab === 'visualization' && this.renderVizTab()}
-            </CustomScrollbar>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-  renderAsBoxes() {
-    const { location } = store.getState();
-    const { panel } = this.props;
-    const activeTab = location.query.tab || 'queries';
-
-    return (
-      <div className="panel-editor-container__editor">
-        <div className="panel-editor-resizer">
-          <div className="panel-editor-resizer__handle">
-            <div className="panel-editor-resizer__handle-dots" />
-          </div>
+          <button className="panel-editor-tabs__close" onClick={this.onClose}>
+            <i className="fa fa-remove" />
+          </button>
         </div>
 
-        <CustomScrollbar>
-          {this.renderQueriesTab()}
-          {this.renderVizTab()}
-        </CustomScrollbar>
+        {activeTab === 'queries' && <QueriesTab panel={panel} dashboard={dashboard} />}
+        {activeTab === 'visualization' && (
+          <VisualizationTab panel={panel} dashboard={dashboard} plugin={plugin} onTypeChanged={onTypeChanged} />
+        )}
       </div>
     );
   }
@@ -196,19 +97,6 @@ interface TabItemParams {
 }
 
 function TabItem({ tab, activeTab, onClick }: TabItemParams) {
-  const tabClasses = classNames({
-    'panel-editor__aside-item': true,
-    active: activeTab === tab.id,
-  });
-
-  return (
-    <a className={tabClasses} onClick={() => onClick(tab)}>
-      <i className={tab.icon} /> {tab.text}
-    </a>
-  );
-}
-
-function OldTabItem({ tab, activeTab, onClick }: TabItemParams) {
   const tabClasses = classNames({
     'gf-tabs-link': true,
     active: activeTab === tab.id,
@@ -216,7 +104,9 @@ function OldTabItem({ tab, activeTab, onClick }: TabItemParams) {
 
   return (
     <li className="gf-tabs-item" onClick={() => onClick(tab)}>
-      <a className={tabClasses}>{tab.text}</a>
+      <a className={tabClasses}>
+        <i className={tab.icon} /> {tab.text}
+      </a>
     </li>
   );
 }

+ 16 - 1
public/app/features/dashboard/dashgrid/QueriesTab.tsx

@@ -3,6 +3,7 @@ import React, { PureComponent } from 'react';
 
 // Services & utils
 import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
+import { EditorTabBody } from './EditorTabBody';
 
 // Types
 import { PanelModel } from '../panel_model';
@@ -48,6 +49,20 @@ export class QueriesTab extends PureComponent<Props> {
   }
 
   render() {
-    return <div ref={element => (this.element = element)} className="panel-height-helper" />;
+    const currentDataSource = {
+      title: 'ProductionDB',
+      imgSrc: 'public/app/plugins/datasource/prometheus/img/prometheus_logo.svg',
+      render: () => {
+        return (
+          <h2>Hello</h2>
+        );
+      },
+    };
+
+    return (
+      <EditorTabBody toolbarItems={[currentDataSource]}>
+        <div ref={element => (this.element = element)} style={{ width: '100%' }} />
+      </EditorTabBody>
+    );
   }
 }

+ 56 - 0
public/app/features/dashboard/dashgrid/VisualizationTab.tsx

@@ -0,0 +1,56 @@
+import React, { PureComponent } from 'react';
+
+import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
+import { EditorTabBody } from './EditorTabBody';
+import { VizTypePicker } from './VizTypePicker';
+
+import { PanelModel } from '../panel_model';
+import { DashboardModel } from '../dashboard_model';
+
+interface Props {
+  panel: PanelModel;
+  dashboard: DashboardModel;
+  plugin: PluginModel;
+  onTypeChanged: (newType: PanelPlugin) => void;
+}
+
+export class VisualizationTab extends PureComponent<Props> {
+
+  constructor(props) {
+    super(props);
+  }
+
+  renderPanelOptions() {
+    const { plugin, panel } = this.props;
+    const { PanelOptionsComponent } = plugin.exports;
+
+    if (PanelOptionsComponent) {
+      return <PanelOptionsComponent options={panel.getOptions()} onChange={this.onPanelOptionsChanged} />;
+    } else {
+      return <p>Visualization has no options</p>;
+    }
+  }
+
+  onPanelOptionsChanged = (options: any) => {
+    this.props.panel.updateOptions(options);
+    this.forceUpdate();
+  };
+
+  render() {
+    const {plugin, onTypeChanged} = this.props;
+
+    const panelSelection = {
+      title: plugin.name,
+      imgSrc: plugin.info.logos.small,
+      render: () => {
+        return <VizTypePicker current={plugin} onTypeChanged={onTypeChanged} />;
+      },
+    };
+
+    return (
+      <EditorTabBody toolbarItems={[panelSelection]}>
+        {this.renderPanelOptions()}
+      </EditorTabBody>
+    );
+  }
+}

+ 8 - 44
public/app/features/dashboard/dashgrid/VizTypePicker.tsx

@@ -2,7 +2,6 @@ import React, { PureComponent } from 'react';
 import classNames from 'classnames';
 import _ from 'lodash';
 
-import { FadeIn } from 'app/core/components/Animations/FadeIn';
 import config from 'app/core/config';
 import { PanelPlugin } from 'app/types/plugins';
 
@@ -13,7 +12,6 @@ interface Props {
 
 interface State {
   pluginList: PanelPlugin[];
-  isOpen: boolean;
 }
 
 export class VizTypePicker extends PureComponent<Props, State> {
@@ -22,7 +20,6 @@ export class VizTypePicker extends PureComponent<Props, State> {
 
     this.state = {
       pluginList: this.getPanelPlugins(''),
-      isOpen: false,
     };
   }
 
@@ -65,53 +62,20 @@ export class VizTypePicker extends PureComponent<Props, State> {
     );
   }
 
-  onToggleOpen = () => {
-    this.setState({ isOpen: !this.state.isOpen });
-  };
-
   render() {
     const { current } = this.props;
-    const { pluginList, isOpen } = this.state;
+    const { pluginList } = this.state;
 
     return (
-      <div className="viz-picker">
-        <div className="viz-picker__bar">
-          <div className="gf-form-inline">
-            <div className="gf-form">
-              <label className="gf-form-label">Visualization</label>
-              <label className="gf-form-input width-10" onClick={this.onToggleOpen}>
-                <span>{current.name}</span>
-                {isOpen && <i className="fa fa-caret-down pull-right" />}
-                {!isOpen && <i className="fa fa-caret-left pull-right" />}
-              </label>
-            </div>
-            <div className="gf-form gf-form--grow">
-              <label className="gf-form-label gf-form-label--grow" />
-            </div>
-            <div className="gf-form">
-              <label className="gf-form-label">
-                <i className="fa fa-fw fa-caret-down" /> Help
-              </label>
-            </div>
-          </div>
+      <>
+        <div className="cta-form__bar">
+          <div className="cta-form__bar-header">Select visualization</div>
+          {this.renderFilters()}
+          <div className="gf-form--grow" />
         </div>
 
-        <FadeIn in={isOpen} duration={300}>
-          <div className="cta-form">
-            <button className="cta-form__close" onClick={this.onToggleOpen}>
-              <i className="fa fa-remove" />
-            </button>
-
-            <div className="cta-form__bar">
-              <div className="cta-form__bar-header">Select visualization</div>
-              {this.renderFilters()}
-              <div className="gf-form--grow" />
-            </div>
-
-            <div className="viz-picker__items">{pluginList.map(this.renderVizPlugin)}</div>
-          </div>
-        </FadeIn>
-      </div>
+        <div className="viz-picker__items">{pluginList.map(this.renderVizPlugin)}</div>
+      </>
     );
   }
 }

+ 1 - 1
public/app/features/panel/partials/metrics_tab.html

@@ -1,4 +1,4 @@
-<div class="gf-form-group">
+<div class="gf-form-group hide">
   <div class="gf-form-inline">
     <div class="gf-form">
       <label class="gf-form-label">Data Source</label>

+ 4 - 0
public/sass/components/_dashboard_grid.scss

@@ -19,6 +19,10 @@
     transform: translate(0px, 0px) !important;
   }
 
+  .panel {
+    margin: 0 !important;
+  }
+
   // Disable grid interaction indicators in fullscreen panels
   .panel-header:hover {
     background-color: inherit;

+ 1 - 1
public/sass/components/_gf-form.scss

@@ -416,7 +416,7 @@ select.gf-form-input ~ .gf-form-help-icon {
   position: absolute;
   right: 0;
   top: -2px;
-  font-size: $font-size-lg;
+  font-size: $font-size-md;
 
   &:hover {
     color: $text-color-strong;

+ 104 - 64
public/sass/components/_viz_editor.scss

@@ -11,16 +11,20 @@
 .panel-editor-container__editor {
   margin-top: $panel-margin*2;
   display: flex;
-  flex-direction: row;
+  flex-direction: column;
   height: 65%;
   position: relative;
 }
 
-.panel-editor__content {
+.panel-editor__scroll {
   flex-grow: 1;
   min-width: 0;
-  height: 100%;
-  padding: 0 20px;
+  display: flex;
+  padding: 0 5px;
+}
+
+.panel-editor__content {
+  padding: 40px 15px;
 }
 
 .panel-in-fullscreen {
@@ -107,66 +111,6 @@
   height: 55px;
 }
 
-.panel-editor__aside {
-  background: $panel-bg;
-  display: flex;
-  flex-direction: column;
-}
-
-.panel-editor__aside-item {
-  padding: 8px 20px;
-  color: $text-color;
-  font-size: $font-size-md;
-  @include left-brand-border;
-
-  &.active {
-    @include left-brand-border-gradient();
-    background: $page-bg;
-  }
-
-  i {
-    width: 23px;
-  }
-
-  .gicon {
-    margin-bottom: 2px;
-  }
-
-  .fa {
-    font-size: 17px;
-  }
-}
-
-.panel-editor__aside-actions {
-  display: flex;
-  flex-direction: column;
-  height: 100%;
-  flex-grow: 1;
-  padding: 60px 15px;
-
-  button {
-    margin-bottom: 10px;
-  }
-}
-
-.panel-editor__aside-header {
-  color: $text-muted;
-  font-size: $font-size-h3;
-  padding: 20px 30px 10px 20px;
-  white-space: nowrap;
-
-  i {
-    font-size: 25px;
-    position: relative;
-    top: 1px;
-    padding-right: 5px;
-  }
-}
-
-.viz-picker__bar {
-  margin-bottom: $spacer;
-}
-
 .panel-editor-resizer {
   position: absolute;
   height: 2px;
@@ -223,3 +167,99 @@
   font-size: $font-size-lg;
   margin-bottom: 20px;
 }
+
+.edit-section {
+  position: relative;
+}
+
+.edit-section__header {
+  display: flex;
+  align-content: center;
+  align-items: center;
+  background: linear-gradient(90deg, #292a2d, black);
+  padding: 7px 30px 7px 20px;
+  box-shadow: 0 0 20px black;
+  cursor: pointer;
+}
+
+.edit-section__selected {
+  padding: $input-padding-y $input-padding-x;
+  font-size: $font-size-md;
+  line-height: $input-line-height;
+  color: $input-color;
+  background-color: $input-bg;
+  border: $input-border;
+  border-radius: $input-border-radius;
+  display: flex;
+  align-items: center;
+  .fa  {
+    margin-left: 20px;
+    display: inline-block;
+    position: relative;
+  }
+}
+
+.edit-section__title {
+  font-size: $font-size-lg;
+  padding-right: 20px;
+  width: 150px;
+}
+
+.edit-section__selected-image {
+  margin-right: 10px;
+  display: inline-block;
+  width: 20px;
+  height: 20px;
+}
+
+.panel-editor-tabs {
+  z-index: 1;
+  box-shadow: $page-header-shadow;
+  border-bottom: 1px solid $page-header-border-color;
+  padding: 0 $dashboard-padding;
+  @include clearfix();
+
+  .active.gf-tabs-link {
+    background: #242427;
+  }
+}
+
+.panel-editor-tabs__close {
+  float: right;
+  padding: 0;
+  margin: 0;
+  background-color: transparent;
+  border: none;
+  padding: $tabs-padding;
+  color: $text-color;
+
+  i {
+    font-size: 120%;
+  }
+  &:hover {
+    color: $text-color-strong;
+  }
+}
+
+.editor-toolbar-view {
+  position: relative;
+  padding: 10px 20px;
+  background-color: $empty-list-cta-bg;
+  border-top: 3px solid $orange;
+  margin: 0 100px;
+}
+
+.editor-toolbar-view__close {
+  background: transparent;
+  padding: 4px 8px 4px 9px;
+  border: none;
+  position: absolute;
+  right: 0;
+  top: -2px;
+  font-size: $font-size-md;
+
+  &:hover {
+    color: $text-color-strong;
+  }
+}
+