Commit ffe03ee2 by Hugo Häggmark

Restructure of component and styling

parent 1aefc4cc
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import { ExploreId } from 'app/types/explore'; import { ExploreId } from 'app/types/explore';
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
import { DataSourceSelectItem, RawTimeRange, TimeRange } from '@grafana/ui'; import { DataSourceSelectItem, RawTimeRange, TimeRange } from '@grafana/ui';
import TimePicker from './TimePicker'; import TimePicker from './TimePicker';
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
interface Props { interface Props {
datasourceMissing: boolean; datasourceMissing: boolean;
...@@ -29,70 +29,136 @@ export class ExploreToolbar extends PureComponent<Props, {}> { ...@@ -29,70 +29,136 @@ export class ExploreToolbar extends PureComponent<Props, {}> {
constructor(props) { constructor(props) {
super(props); super(props);
this.timepickerRef = React.createRef(); 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() { render() {
const { const { datasourceMissing, exploreId, loading, range, splitted } = this.props;
datasourceMissing, const toolbar = this.createSplittedClassName('toolbar');
exploreDatasources, const toolbarItem = this.createSplittedClassName('toolbar-item');
exploreId, const toolbarHeader = this.createSplittedClassName('toolbar-header');
loading, const timepickerLarge = this.createSplittedClassName('toolbar-content-item timepicker-large-screens');
range, const timepickerSmall = this.createSplittedClassName('toolbar-content-item timepicker-small-screens');
selectedDatasource,
splitted,
} = this.props;
return ( return (
<div className="navbar"> <div className={toolbar}>
{exploreId === 'left' ? ( <div className={toolbarItem}>
<div> <div className={toolbarHeader}>
<a className="navbar-page-btn"> <div className="toolbar-header-title">
<i className="fa fa-rocket" /> {exploreId === 'left' && (
Explore <a className="navbar-page-btn">
</a> <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>
) : ( </div>
<> <div className={toolbarItem}>
<div className="navbar-page-btn" /> {!datasourceMissing && splitted ? (
<div className="navbar-buttons explore-first-button"> <div className="datasource-picker">{this.createDatasourcePicker()}</div>
<button className="btn navbar-button" onClick={this.props.onCloseSplit}> ) : null}
Close Split </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> </button>
</div> </div>
</> <div className="toolbar-content-item">
)} {this.createResponsiveButton({
{!datasourceMissing ? ( title: 'Run Query',
<div className="navbar-buttons"> onClick: this.props.onRunQuery,
<DataSourcePicker buttonClassName: 'navbar-button--primary',
onChange={this.props.onChangeDatasource} iconClassName: loading ? 'fa fa-spinner fa-fw fa-spin run-icon' : 'fa fa-level-down fa-fw run-icon',
datasources={exploreDatasources} })}
current={selectedDatasource} </div>
/>
</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> </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>
</div> </div>
); );
......
...@@ -39,6 +39,7 @@ interface TimePickerProps { ...@@ -39,6 +39,7 @@ interface TimePickerProps {
isUtc?: boolean; isUtc?: boolean;
range?: RawTimeRange; range?: RawTimeRange;
onChangeTime?: (range: RawTimeRange, scanning?: boolean) => void; onChangeTime?: (range: RawTimeRange, scanning?: boolean) => void;
iconOnly?: boolean;
} }
interface TimePickerState { interface TimePickerState {
...@@ -292,19 +293,27 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke ...@@ -292,19 +293,27 @@ export default class TimePicker extends PureComponent<TimePickerProps, TimePicke
} }
render() { render() {
const { iconOnly } = this.props;
const { isUtc, rangeString, refreshInterval } = this.state; const { isUtc, rangeString, refreshInterval } = this.state;
return ( return (
<div className="timepicker"> <div className="timepicker">
<div className="navbar-buttons"> <div className="navbar-buttons">
<button className="btn navbar-button navbar-button--tight timepicker-left" onClick={this.handleClickLeft}> <button className="btn navbar-button navbar-button--tight timepicker-left" onClick={this.handleClickLeft}>
<i className="fa fa-chevron-left" /> <i className="fa fa-chevron-left" />
</button> </button>
<button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}> {iconOnly ? (
<i className="fa fa-clock-o" /> <button className="btn navbar-button gf-timepicker-nav-btn" onClick={this.handleClickPicker}>
<span className="timepicker-rangestring">{rangeString}</span> <i className="fa fa-clock-o" />
{isUtc ? <span className="gf-timepicker-utc">UTC</span> : null} </button>
{refreshInterval ? <span className="text-warning">&nbsp; Refresh every {refreshInterval}</span> : null} ) : (
</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}> <button className="btn navbar-button navbar-button--tight timepicker-right" onClick={this.handleClickRight}>
<i className="fa fa-chevron-right" /> <i className="fa fa-chevron-right" />
</button> </button>
......
.explore { .timepicker-small-screens,
flex: 1 1 auto; .timepicker-small-screens-splitted,
.small-screens {
display: none;
}
&-container { .datasource-picker {
padding: $dashboard-padding; min-width: 200px;
} max-width: 200px;
}
&-wrapper { .toolbar-splitted,
display: flex; .toolbar {
display: flex;
background: inherit;
justify-content: space-between;
height: auto;
padding: 2px $dashboard-padding;
}
> .explore-split { .toolbar {
width: 50%; flex-flow: row nowrap;
} }
}
// Push split button a bit .toolbar-splitted {
.explore-first-button { flex-flow: row wrap;
margin-left: 15px; }
}
.explore-panel { .toolbar-item-splitted,
margin-top: $panel-margin; .toolbar-item {
} align-self: center;
}
.explore-panel__body { .toolbar-item-splitted:first-child {
padding: $panel-padding; flex: 1 1 100%;
} }
.explore-panel__header { .toolbar-header-splitted,
padding: $panel-padding; .toolbar-header {
padding-top: 5px; display: flex;
padding-bottom: 0; flex: 1 1 0;
display: flex; flex-flow: row nowrap;
cursor: pointer; font-size: 18px;
margin-bottom: 5px; min-height: 55px;
transition: all 0.1s linear; line-height: 55px;
} justify-content: space-between;
}
.explore-panel__header-label { .toolbar-header {
font-weight: 500; align-items: center;
margin-right: $panel-margin; }
font-size: $font-size-h6;
box-shadow: $text-shadow-faint;
}
.explore-panel__header-buttons { .toolbar-header-datasource {
margin-right: $panel-margin; padding-left: $dashboard-padding;
font-size: $font-size-lg; flex: 2 1 auto;
line-height: $font-size-h6; }
}
// Make sure wrap buttons around on small screens .toolbar-header-close {
.navbar { color: #d6d6d6;
flex-wrap: wrap; padding-right: 8px;
height: auto; }
}
.navbar-page-btn { .toolbar-content-splitted,
margin-right: 1rem; .toolbar-content {
display: flex;
flex: 2 1 0;
flex-flow: row wrap;
justify-content: flex-end;
align-items: center;
}
// Explore icon in header .toolbar-content-item {
.fa { padding: 10px 2px;
font-size: 100%; }
opacity: 0.75;
margin-right: 0.5em;
}
}
// Toggle mode @media only screen and (max-width: 1545px) {
.navbar-button.active { .timepicker-large-screens-splitted {
color: $btn-active-text-color; display: none;
background-color: $btn-active-bg;
} }
.navbar-button--no-icon { .timepicker-small-screens-splitted {
line-height: 18px; display: inline-block;
} }
}
.result-options { @media only screen and (max-width: 1070px) {
margin: 2 * $panel-margin 0; .timepicker-large-screens {
display: none;
} }
.time-series-disclaimer { .timepicker-small-screens {
width: 300px; display: inline-block;
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 { @media only screen and (max-width: 768px) {
position: absolute; .large-screens {
left: 0; display: none;
right: 0;
top: 3.5rem;
text-align: center;
font-size: 0.8rem;
} }
.graph-legend { .small-screens {
flex-wrap: wrap; display: inline-block;
} }
.explore-panel__loader { .toolbar {
height: 2px; flex-flow: row wrap;
position: relative;
overflow: hidden;
background: none;
margin: $panel-margin / 2;
transition: background-color 1s ease;
} }
.explore-panel__loader--active { .toolbar-content {
background: $text-color-faint; align-self: flex-start;
justify-content: flex-start;
} }
.explore-panel__loader--active:after { .toolbar-content-item {
content: ' '; padding: 5px 2px;
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 { .datasource-picker > div > .ds-picker {
from { min-width: 160px;
left: -25%; max-width: 160px;
}
to {
left: 100%;
}
} }
}
.datasource-picker { .explore {
min-width: 200px; flex: 1 1 auto;
} }
.timepicker { .explore + .explore {
display: flex; border-left: 1px dotted $table-border;
}
&-rangestring { .explore-container {
margin-left: 0.5em; padding: $dashboard-padding;
} }
}
.explore-wrapper {
display: flex;
.run-icon { > .explore-split {
margin-left: 0.25em; width: 50%;
transform: rotate(90deg);
} }
}
.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 { .time-series-disclaimer {
position: relative; 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 { .show-all-time-series {
text-decoration: underline; cursor: pointer;
color: $external-link-color;
} }
} }
.explore + .explore { .navbar .elapsed-time {
border-left: 1px dotted $table-border; 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 { .query-row {
...@@ -352,3 +418,178 @@ ...@@ -352,3 +418,178 @@
margin: $panel-margin/2 0; margin: $panel-margin/2 0;
cursor: pointer; 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;
// }
// }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment