ソースを参照

Restructure of component and styling

Hugo Häggmark 7 年 前
コミット
ffe03ee22d

+ 122 - 56
public/app/features/explore/ExploreToolbar.tsx

@@ -1,8 +1,8 @@
 import React, { PureComponent } from 'react';
 import { ExploreId } from 'app/types/explore';
-import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
 import { DataSourceSelectItem, RawTimeRange, TimeRange } from '@grafana/ui';
 import TimePicker from './TimePicker';
+import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
 
 interface Props {
   datasourceMissing: boolean;
@@ -29,70 +29,136 @@ export class ExploreToolbar extends PureComponent<Props, {}> {
   constructor(props) {
     super(props);
     this.timepickerRef = React.createRef();
+    this.createResponsiveButton = this.createResponsiveButton.bind(this);
+    this.createDatasourcePicker = this.createDatasourcePicker.bind(this);
+    this.createSplittedClassName = this.createSplittedClassName.bind(this);
+  }
+
+  createDatasourcePicker() {
+    const { exploreDatasources, selectedDatasource } = this.props;
+
+    return (
+      <DataSourcePicker
+        onChange={this.props.onChangeDatasource}
+        datasources={exploreDatasources}
+        current={selectedDatasource}
+      />
+    );
+  }
+
+  createResponsiveButton(options: {
+    title: string;
+    onClick: () => void;
+    buttonClassName?: string;
+    iconClassName?: string;
+  }) {
+    const { splitted } = this.props;
+    const { title, onClick, buttonClassName, iconClassName } = options;
+
+    return (
+      <>
+        <button className={`btn navbar-button large-screens ${buttonClassName && buttonClassName}`} onClick={onClick}>
+          {!splitted ? title : ''}
+          {iconClassName && <i className={iconClassName} />}
+        </button>
+        <button className={`btn navbar-button small-screens ${buttonClassName && buttonClassName}`} onClick={onClick}>
+          {iconClassName && <i className={iconClassName} />}
+        </button>
+      </>
+    );
+  }
+
+  createSplittedClassName(className: string) {
+    const { splitted } = this.props;
+
+    return splitted ? `${className}-splitted` : className;
   }
 
   render() {
-    const {
-      datasourceMissing,
-      exploreDatasources,
-      exploreId,
-      loading,
-      range,
-      selectedDatasource,
-      splitted,
-    } = this.props;
+    const { datasourceMissing, exploreId, loading, range, splitted } = this.props;
+    const toolbar = this.createSplittedClassName('toolbar');
+    const toolbarItem = this.createSplittedClassName('toolbar-item');
+    const toolbarHeader = this.createSplittedClassName('toolbar-header');
+    const timepickerLarge = this.createSplittedClassName('toolbar-content-item timepicker-large-screens');
+    const timepickerSmall = this.createSplittedClassName('toolbar-content-item timepicker-small-screens');
 
     return (
-      <div className="navbar">
-        {exploreId === 'left' ? (
-          <div>
-            <a className="navbar-page-btn">
-              <i className="fa fa-rocket" />
-              Explore
-            </a>
+      <div className={toolbar}>
+        <div className={toolbarItem}>
+          <div className={toolbarHeader}>
+            <div className="toolbar-header-title">
+              {exploreId === 'left' && (
+                <a className="navbar-page-btn">
+                  <i className="fa fa-rocket fa-fw" />
+                  Explore
+                </a>
+              )}
+            </div>
+            <div className="toolbar-header-datasource large-screens">
+              <div className="datasource-picker">
+                {!datasourceMissing && !splitted ? this.createDatasourcePicker() : null}
+              </div>
+            </div>
+            <div className="toolbar-header-close">
+              {exploreId === 'right' && (
+                <a onClick={this.props.onCloseSplit}>
+                  <i className="fa fa-times" />
+                </a>
+              )}
+            </div>
           </div>
-        ) : (
-          <>
-            <div className="navbar-page-btn" />
-            <div className="navbar-buttons explore-first-button">
-              <button className="btn navbar-button" onClick={this.props.onCloseSplit}>
-                Close Split
+        </div>
+        <div className={toolbarItem}>
+          {!datasourceMissing && splitted ? (
+            <div className="datasource-picker">{this.createDatasourcePicker()}</div>
+          ) : null}
+        </div>
+        <div className={toolbarItem}>
+          <div className="toolbar-content">
+            {!datasourceMissing && !splitted ? (
+              <div className="toolbar-content-item small-screens">
+                <div className="datasource-picker">{this.createDatasourcePicker()}</div>
+              </div>
+            ) : null}
+            {exploreId === 'left' && !splitted ? (
+              <div className="toolbar-content-item">
+                {this.createResponsiveButton({
+                  title: 'Split',
+                  onClick: this.props.onSplit,
+                  iconClassName: 'fa fa-fw fa-columns',
+                })}
+              </div>
+            ) : null}
+            <div className={timepickerLarge}>
+              <TimePicker
+                ref={this.timepickerRef}
+                range={range}
+                onChangeTime={this.props.onChangeTime}
+                iconOnly={false}
+              />
+            </div>
+            <div className={timepickerSmall}>
+              <TimePicker
+                ref={this.timepickerRef}
+                range={range}
+                onChangeTime={this.props.onChangeTime}
+                iconOnly={true}
+              />
+            </div>
+            <div className="toolbar-content-item">
+              <button className="btn navbar-button navbar-button--no-icon" onClick={this.props.onClearAll}>
+                Clear All
               </button>
             </div>
-          </>
-        )}
-        {!datasourceMissing ? (
-          <div className="navbar-buttons">
-            <DataSourcePicker
-              onChange={this.props.onChangeDatasource}
-              datasources={exploreDatasources}
-              current={selectedDatasource}
-            />
-          </div>
-        ) : null}
-        <div className="navbar__spacer" />
-        {exploreId === 'left' && !splitted ? (
-          <div className="navbar-buttons">
-            <button className="btn navbar-button" onClick={this.props.onSplit}>
-              Split
-            </button>
+            <div className="toolbar-content-item">
+              {this.createResponsiveButton({
+                title: 'Run Query',
+                onClick: this.props.onRunQuery,
+                buttonClassName: 'navbar-button--primary',
+                iconClassName: loading ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon',
+              })}
+            </div>
           </div>
-        ) : null}
-        <TimePicker ref={this.timepickerRef} range={range} onChangeTime={this.props.onChangeTime} />
-        <div className="navbar-buttons">
-          <button className="btn navbar-button navbar-button--no-icon" onClick={this.props.onClearAll}>
-            Clear All
-          </button>
-        </div>
-        <div className="navbar-buttons relative">
-          <button className="btn navbar-button navbar-button--primary" onClick={this.props.onRunQuery}>
-            Run Query{' '}
-            {loading ? (
-              <i className="fa fa-spinner fa-fw fa-spin run-icon" />
-            ) : (
-              <i className="fa fa-level-down fa-fw run-icon" />
-            )}
-          </button>
         </div>
       </div>
     );

+ 15 - 6
public/app/features/explore/TimePicker.tsx

@@ -39,6 +39,7 @@ interface TimePickerProps {
   isUtc?: boolean;
   range?: RawTimeRange;
   onChangeTime?: (range: RawTimeRange, scanning?: boolean) => void;
+  iconOnly?: boolean;
 }
 
 interface TimePickerState {
@@ -292,19 +293,27 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
   }
 
   render() {
+    const { iconOnly } = this.props;
     const { isUtc, rangeString, refreshInterval } = this.state;
+
     return (
       <div className="timepicker">
         <div className="navbar-buttons">
           <button className="btn navbar-button navbar-button--tight timepicker-left" onClick={this.handleClickLeft}>
             <i className="fa fa-chevron-left" />
           </button>
-          <button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}>
-            <i className="fa fa-clock-o" />
-            <span className="timepicker-rangestring">{rangeString}</span>
-            {isUtc ? <span className="gf-timepicker-utc">UTC</span> : null}
-            {refreshInterval ? <span className="text-warning">&nbsp; Refresh every {refreshInterval}</span> : null}
-          </button>
+          {iconOnly ? (
+            <button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}>
+              <i className="fa fa-clock-o" />
+            </button>
+          ) : (
+            <button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}>
+              <i className="fa fa-clock-o" />
+              <span className="timepicker-rangestring">{rangeString}</span>
+              {isUtc ? <span className="gf-timepicker-utc">UTC</span> : null}
+              {refreshInterval ? <span className="text-warning">&nbsp; Refresh every {refreshInterval}</span> : null}
+            </button>
+          )}
           <button className="btn navbar-button navbar-button--tight timepicker-right" onClick={this.handleClickRight}>
             <i className="fa fa-chevron-right" />
           </button>

+ 375 - 134
public/sass/pages/_explore.scss

@@ -1,180 +1,246 @@
-.explore {
-  flex: 1 1 auto;
+.timepicker-small-screens,
+.timepicker-small-screens-splitted,
+.small-screens {
+  display: none;
+}
 
-  &-container {
-    padding: $dashboard-padding;
-  }
+.datasource-picker {
+  min-width: 200px;
+  max-width: 200px;
+}
 
-  &-wrapper {
-    display: flex;
+.toolbar-splitted,
+.toolbar {
+  display: flex;
+  background: inherit;
+  justify-content: space-between;
+  height: auto;
+  padding: 2px $dashboard-padding;
+}
 
-    > .explore-split {
-      width: 50%;
-    }
-  }
+.toolbar {
+  flex-flow: row nowrap;
+}
 
-  // Push split button a bit
-  .explore-first-button {
-    margin-left: 15px;
-  }
+.toolbar-splitted {
+  flex-flow: row wrap;
+}
 
-  .explore-panel {
-    margin-top: $panel-margin;
-  }
+.toolbar-item-splitted,
+.toolbar-item {
+  align-self: center;
+}
 
-  .explore-panel__body {
-    padding: $panel-padding;
-  }
+.toolbar-item-splitted:first-child {
+  flex: 1 1 100%;
+}
 
-  .explore-panel__header {
-    padding: $panel-padding;
-    padding-top: 5px;
-    padding-bottom: 0;
-    display: flex;
-    cursor: pointer;
-    margin-bottom: 5px;
-    transition: all 0.1s linear;
-  }
+.toolbar-header-splitted,
+.toolbar-header {
+  display: flex;
+  flex: 1 1 0;
+  flex-flow: row nowrap;
+  font-size: 18px;
+  min-height: 55px;
+  line-height: 55px;
+  justify-content: space-between;
+}
 
-  .explore-panel__header-label {
-    font-weight: 500;
-    margin-right: $panel-margin;
-    font-size: $font-size-h6;
-    box-shadow: $text-shadow-faint;
-  }
+.toolbar-header {
+  align-items: center;
+}
 
-  .explore-panel__header-buttons {
-    margin-right: $panel-margin;
-    font-size: $font-size-lg;
-    line-height: $font-size-h6;
-  }
+.toolbar-header-datasource {
+  padding-left: $dashboard-padding;
+  flex: 2 1 auto;
+}
 
-  // Make sure wrap buttons around on small screens
-  .navbar {
-    flex-wrap: wrap;
-    height: auto;
-  }
+.toolbar-header-close {
+  color: #d6d6d6;
+  padding-right: 8px;
+}
 
-  .navbar-page-btn {
-    margin-right: 1rem;
+.toolbar-content-splitted,
+.toolbar-content {
+  display: flex;
+  flex: 2 1 0;
+  flex-flow: row wrap;
+  justify-content: flex-end;
+  align-items: center;
+}
 
-    // Explore icon in header
-    .fa {
-      font-size: 100%;
-      opacity: 0.75;
-      margin-right: 0.5em;
-    }
-  }
+.toolbar-content-item {
+  padding: 10px 2px;
+}
 
-  // Toggle mode
-  .navbar-button.active {
-    color: $btn-active-text-color;
-    background-color: $btn-active-bg;
+@media only screen and (max-width: 1545px) {
+  .timepicker-large-screens-splitted {
+    display: none;
   }
 
-  .navbar-button--no-icon {
-    line-height: 18px;
+  .timepicker-small-screens-splitted {
+    display: inline-block;
   }
+}
 
-  .result-options {
-    margin: 2 * $panel-margin 0;
+@media only screen and (max-width: 1070px) {
+  .timepicker-large-screens {
+    display: none;
   }
 
-  .time-series-disclaimer {
-    width: 300px;
-    margin: $panel-margin auto;
-    padding: 10px 0;
-    border-radius: $border-radius;
-    text-align: center;
-    background-color: $panel-bg;
-
-    .disclaimer-icon {
-      color: $yellow;
-      margin-right: $panel-margin/2;
-    }
-
-    .show-all-time-series {
-      cursor: pointer;
-      color: $external-link-color;
-    }
+  .timepicker-small-screens {
+    display: inline-block;
   }
+}
 
-  .navbar .elapsed-time {
-    position: absolute;
-    left: 0;
-    right: 0;
-    top: 3.5rem;
-    text-align: center;
-    font-size: 0.8rem;
+@media only screen and (max-width: 768px) {
+  .large-screens {
+    display: none;
   }
 
-  .graph-legend {
-    flex-wrap: wrap;
+  .small-screens {
+    display: inline-block;
   }
 
-  .explore-panel__loader {
-    height: 2px;
-    position: relative;
-    overflow: hidden;
-    background: none;
-    margin: $panel-margin / 2;
-    transition: background-color 1s ease;
+  .toolbar {
+    flex-flow: row wrap;
   }
 
-  .explore-panel__loader--active {
-    background: $text-color-faint;
+  .toolbar-content {
+    align-self: flex-start;
+    justify-content: flex-start;
   }
 
-  .explore-panel__loader--active:after {
-    content: ' ';
-    display: block;
-    width: 25%;
-    top: 0;
-    top: -50%;
-    height: 250%;
-    position: absolute;
-    animation: loader 2s cubic-bezier(0.17, 0.67, 0.83, 0.67);
-    animation-iteration-count: 100;
-    background: $blue;
+  .toolbar-content-item {
+    padding: 5px 2px;
   }
 
-  @keyframes loader {
-    from {
-      left: -25%;
-    }
-    to {
-      left: 100%;
-    }
+  .datasource-picker > div > .ds-picker {
+    min-width: 160px;
+    max-width: 160px;
   }
+}
 
-  .datasource-picker {
-    min-width: 200px;
-  }
+.explore {
+  flex: 1 1 auto;
+}
 
-  .timepicker {
-    display: flex;
+.explore + .explore {
+  border-left: 1px dotted $table-border;
+}
 
-    &-rangestring {
-      margin-left: 0.5em;
-    }
-  }
+.explore-container {
+  padding: $dashboard-padding;
+}
+
+.explore-wrapper {
+  display: flex;
 
-  .run-icon {
-    margin-left: 0.25em;
-    transform: rotate(90deg);
+  > .explore-split {
+    width: 50%;
   }
+}
+
+.explore-panel {
+  margin-top: $panel-margin;
+}
+
+.explore-panel__body {
+  padding: $panel-padding;
+}
+
+.explore-panel__header {
+  padding: $panel-padding;
+  padding-top: 5px;
+  padding-bottom: 0;
+  display: flex;
+  cursor: pointer;
+  margin-bottom: 5px;
+  transition: all 0.1s linear;
+}
+
+.explore-panel__header-label {
+  font-weight: 500;
+  margin-right: $panel-margin;
+  font-size: $font-size-h6;
+  box-shadow: $text-shadow-faint;
+}
+
+.explore-panel__header-buttons {
+  margin-right: $panel-margin;
+  font-size: $font-size-lg;
+  line-height: $font-size-h6;
+}
+
+.result-options {
+  margin: 2 * $panel-margin 0;
+}
 
-  .relative {
-    position: relative;
+.time-series-disclaimer {
+  width: 300px;
+  margin: $panel-margin auto;
+  padding: 10px 0;
+  border-radius: $border-radius;
+  text-align: center;
+  background-color: $panel-bg;
+
+  .disclaimer-icon {
+    color: $yellow;
+    margin-right: $panel-margin/2;
   }
 
-  .link {
-    text-decoration: underline;
+  .show-all-time-series {
+    cursor: pointer;
+    color: $external-link-color;
   }
 }
 
-.explore + .explore {
-  border-left: 1px dotted $table-border;
+.navbar .elapsed-time {
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 3.5rem;
+  text-align: center;
+  font-size: 0.8rem;
+}
+
+.graph-legend {
+  flex-wrap: wrap;
+}
+
+.explore-panel__loader {
+  height: 2px;
+  position: relative;
+  overflow: hidden;
+  background: none;
+  margin: $panel-margin / 2;
+  transition: background-color 1s ease;
+}
+
+.explore-panel__loader--active {
+  background: $text-color-faint;
+}
+
+.explore-panel__loader--active:after {
+  content: ' ';
+  display: block;
+  width: 25%;
+  top: 0;
+  top: -50%;
+  height: 250%;
+  position: absolute;
+  animation: loader 2s cubic-bezier(0.17, 0.67, 0.83, 0.67);
+  animation-iteration-count: 100;
+  background: $blue;
+}
+
+@keyframes loader {
+  from {
+    left: -25%;
+  }
+  to {
+    left: 100%;
+  }
 }
 
 .query-row {
@@ -352,3 +418,178 @@
   margin: $panel-margin/2 0;
   cursor: pointer;
 }
+
+// .explore {
+//   flex: 1 1 auto;
+
+//   &-container {
+//     padding: $dashboard-padding;
+//   }
+
+//   &-wrapper {
+//     display: flex;
+
+//     > .explore-split {
+//       width: 50%;
+//     }
+//   }
+
+//   // Push split button a bit
+//   .explore-first-button {
+//     margin-left: 15px;
+//   }
+
+//   .explore-panel {
+//     margin-top: $panel-margin;
+//   }
+
+//   .explore-panel__body {
+//     padding: $panel-padding;
+//   }
+
+//   .explore-panel__header {
+//     padding: $panel-padding;
+//     padding-top: 5px;
+//     padding-bottom: 0;
+//     display: flex;
+//     cursor: pointer;
+//     margin-bottom: 5px;
+//     transition: all 0.1s linear;
+//   }
+
+//   .explore-panel__header-label {
+//     font-weight: 500;
+//     margin-right: $panel-margin;
+//     font-size: $font-size-h6;
+//     box-shadow: $text-shadow-faint;
+//   }
+
+//   .explore-panel__header-buttons {
+//     margin-right: $panel-margin;
+//     font-size: $font-size-lg;
+//     line-height: $font-size-h6;
+//   }
+
+//   // Make sure wrap buttons around on small screens
+//   .navbar {
+//     flex-wrap: wrap;
+//     height: auto;
+//   }
+
+//   .navbar-page-btn {
+//     margin-right: 1rem;
+
+//     // Explore icon in header
+//     .fa {
+//       font-size: 100%;
+//       opacity: 0.75;
+//       margin-right: 0.5em;
+//     }
+//   }
+
+//   // Toggle mode
+//   .navbar-button.active {
+//     color: $btn-active-text-color;
+//     background-color: $btn-active-bg;
+//   }
+
+//   .navbar-button--no-icon {
+//     line-height: 18px;
+//   }
+
+//   .result-options {
+//     margin: 2 * $panel-margin 0;
+//   }
+
+//   .time-series-disclaimer {
+//     width: 300px;
+//     margin: $panel-margin auto;
+//     padding: 10px 0;
+//     border-radius: $border-radius;
+//     text-align: center;
+//     background-color: $panel-bg;
+
+//     .disclaimer-icon {
+//       color: $yellow;
+//       margin-right: $panel-margin/2;
+//     }
+
+//     .show-all-time-series {
+//       cursor: pointer;
+//       color: $external-link-color;
+//     }
+//   }
+
+//   .navbar .elapsed-time {
+//     position: absolute;
+//     left: 0;
+//     right: 0;
+//     top: 3.5rem;
+//     text-align: center;
+//     font-size: 0.8rem;
+//   }
+
+//   .graph-legend {
+//     flex-wrap: wrap;
+//   }
+
+//   .explore-panel__loader {
+//     height: 2px;
+//     position: relative;
+//     overflow: hidden;
+//     background: none;
+//     margin: $panel-margin / 2;
+//     transition: background-color 1s ease;
+//   }
+
+//   .explore-panel__loader--active {
+//     background: $text-color-faint;
+//   }
+
+//   .explore-panel__loader--active:after {
+//     content: ' ';
+//     display: block;
+//     width: 25%;
+//     top: 0;
+//     top: -50%;
+//     height: 250%;
+//     position: absolute;
+//     animation: loader 2s cubic-bezier(0.17, 0.67, 0.83, 0.67);
+//     animation-iteration-count: 100;
+//     background: $blue;
+//   }
+
+//   @keyframes loader {
+//     from {
+//       left: -25%;
+//     }
+//     to {
+//       left: 100%;
+//     }
+//   }
+
+//   .datasource-picker {
+//     min-width: 200px;
+//   }
+
+//   .timepicker {
+//     display: flex;
+
+//     &-rangestring {
+//       margin-left: 0.5em;
+//     }
+//   }
+
+//   .run-icon {
+//     margin-left: 0.25em;
+//     transform: rotate(90deg);
+//   }
+
+//   .relative {
+//     position: relative;
+//   }
+
+//   .link {
+//     text-decoration: underline;
+//   }
+// }