Merge branch 'master' into feature/reverse-kapa
commit
2851d607cc
|
@ -10,11 +10,13 @@
|
|||
1. [#1382](https://github.com/influxdata/chronograf/pull/1382): Add line-protocol proxy for InfluxDB data sources
|
||||
1. [#1391](https://github.com/influxdata/chronograf/pull/1391): :dashboardTime: - Support cell-specific time ranges
|
||||
1. [#1201](https://github.com/influxdata/chronograf/pull/1201): Allow chronograf to enable/disable all tickscripts.
|
||||
1. [#1401](https://github.com/influxdata/chronograf/pull/1401): Add support for kapacitor config deletion.
|
||||
|
||||
### UI Improvements
|
||||
1. [#1378](https://github.com/influxdata/chronograf/pull/1378): Save query time range for dashboards
|
||||
1. [#1365](https://github.com/influxdata/chronograf/pull/1365): Show red indicator on Hosts Page for an offline host
|
||||
1. [#1373](https://github.com/influxdata/chronograf/pull/1373): Re-address dashboard cell stacking contexts
|
||||
1. [#1385](https://github.com/influxdata/chronograf/pull/1385): Combined Measurements & Tags columns within the Data Explorer, feels more spacious and intuitive. New design for applying functions to Fields.
|
||||
1. [#602](https://github.com/influxdata/chronograf/pull/602): Normalize terminology in app
|
||||
1. [#1392](https://github.com/influxdata/chronograf/pull/1392): Overlays are now full screen
|
||||
1. [#1395](https://github.com/influxdata/chronograf/pull/1395): Change default global time range to past 1 hour
|
||||
|
|
|
@ -3,9 +3,7 @@ import React, {Component, PropTypes} from 'react'
|
|||
import _ from 'lodash'
|
||||
import uuid from 'node-uuid'
|
||||
|
||||
import ResizeContainer, {
|
||||
ResizeBottom,
|
||||
} from 'src/shared/components/ResizeContainer'
|
||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||
import QueryMaker from 'src/data_explorer/components/QueryMaker'
|
||||
import Visualization from 'src/data_explorer/components/Visualization'
|
||||
import OverlayControls from 'src/dashboards/components/OverlayControls'
|
||||
|
@ -14,6 +12,7 @@ import * as queryModifiers from 'src/utils/queryTransitions'
|
|||
import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import {getQueryConfig} from 'shared/apis'
|
||||
import {MINIMUM_HEIGHTS} from 'src/data_explorer/constants'
|
||||
|
||||
class CellEditorOverlay extends Component {
|
||||
constructor(props) {
|
||||
|
@ -152,8 +151,12 @@ class CellEditorOverlay extends Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="data-explorer overlay-technology">
|
||||
<ResizeContainer>
|
||||
<div className="overlay-technology">
|
||||
<ResizeContainer
|
||||
containerClass="resizer--full-size"
|
||||
minTopHeight={MINIMUM_HEIGHTS.visualization}
|
||||
minBottomHeight={MINIMUM_HEIGHTS.queryMaker}
|
||||
>
|
||||
<Visualization
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
|
@ -165,29 +168,25 @@ class CellEditorOverlay extends Component {
|
|||
editQueryStatus={editQueryStatus}
|
||||
views={[]}
|
||||
/>
|
||||
<ResizeBottom>
|
||||
<div
|
||||
style={{display: 'flex', flexDirection: 'column', height: '100%'}}
|
||||
>
|
||||
<OverlayControls
|
||||
selectedGraphType={cellWorkingType}
|
||||
onSelectGraphType={this.handleSelectGraphType}
|
||||
onCancel={onCancel}
|
||||
onSave={this.handleSaveCell}
|
||||
/>
|
||||
<QueryMaker
|
||||
source={source}
|
||||
templates={templates}
|
||||
queries={queriesWorkingDraft}
|
||||
actions={queryActions}
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
setActiveQueryIndex={this.handleSetActiveQueryIndex}
|
||||
onDeleteQuery={this.handleDeleteQuery}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
/>
|
||||
</div>
|
||||
</ResizeBottom>
|
||||
<div className="overlay-technology--editor">
|
||||
<OverlayControls
|
||||
selectedGraphType={cellWorkingType}
|
||||
onSelectGraphType={this.handleSelectGraphType}
|
||||
onCancel={onCancel}
|
||||
onSave={this.handleSaveCell}
|
||||
/>
|
||||
<QueryMaker
|
||||
source={source}
|
||||
templates={templates}
|
||||
queries={queriesWorkingDraft}
|
||||
actions={queryActions}
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
setActiveQueryIndex={this.handleSetActiveQueryIndex}
|
||||
onDeleteQuery={this.handleDeleteQuery}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
/>
|
||||
</div>
|
||||
</ResizeContainer>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import uuid from 'node-uuid'
|
||||
|
||||
import TemplateVariableTable
|
||||
|
@ -34,7 +34,7 @@ const TemplateVariableManager = ({
|
|||
Add Variable
|
||||
</button>
|
||||
<button
|
||||
className={classNames('btn btn-success btn-sm', {
|
||||
className={classnames('btn btn-success btn-sm', {
|
||||
disabled: !isEdited,
|
||||
})}
|
||||
type="button"
|
||||
|
|
|
@ -3,7 +3,7 @@ import {connect} from 'react-redux'
|
|||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import OnClickOutside from 'react-onclickoutside'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
import DeleteConfirmButtons from 'shared/components/DeleteConfirmButtons'
|
||||
|
@ -115,7 +115,7 @@ const TemplateVariableRow = ({
|
|||
onErrorThrown,
|
||||
}) => (
|
||||
<form
|
||||
className={classNames('template-variable-manager--table-row', {
|
||||
className={classnames('template-variable-manager--table-row', {
|
||||
editing: isEditing,
|
||||
})}
|
||||
onSubmit={onSubmit({
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import {showDatabases, showRetentionPolicies} from 'shared/apis/metaQuery'
|
||||
import showDatabasesParser from 'shared/parsing/showDatabases'
|
||||
import showRetentionPoliciesParser from 'shared/parsing/showRetentionPolicies'
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
const DatabaseDropdown = React.createClass({
|
||||
propTypes: {
|
||||
query: shape({}).isRequired,
|
||||
onChooseNamespace: func.isRequired,
|
||||
},
|
||||
|
||||
contextTypes: {
|
||||
source: shape({
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
namespaces: [],
|
||||
}
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
const {source} = this.context
|
||||
const proxy = source.links.proxy
|
||||
showDatabases(proxy).then(resp => {
|
||||
const {errors, databases} = showDatabasesParser(resp.data)
|
||||
if (errors.length) {
|
||||
// do something
|
||||
}
|
||||
|
||||
const namespaces = []
|
||||
showRetentionPolicies(proxy, databases).then(res => {
|
||||
res.data.results.forEach((result, index) => {
|
||||
const {errors: errs, retentionPolicies} = showRetentionPoliciesParser(
|
||||
result
|
||||
)
|
||||
if (errs.length) {
|
||||
// do something
|
||||
}
|
||||
|
||||
retentionPolicies.forEach(rp => {
|
||||
namespaces.push({
|
||||
database: databases[index],
|
||||
retentionPolicy: rp.name,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
this.setState({namespaces})
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
render() {
|
||||
const {query, onChooseNamespace} = this.props
|
||||
const {namespaces} = this.state
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
className="dropdown-160 query-builder--db-dropdown"
|
||||
items={namespaces.map(n => ({...n, text: `${n.database}.${n.retentionPolicy}`}))}
|
||||
onChoose={onChooseNamespace}
|
||||
selected={(query.database && query.retentionPolicy) ? `${query.database}.${query.retentionPolicy}` : 'Choose a DB & RP'}
|
||||
/>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export default DatabaseDropdown
|
|
@ -1,21 +1,23 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import {showDatabases, showRetentionPolicies} from 'shared/apis/metaQuery'
|
||||
import showDatabasesParser from 'shared/parsing/showDatabases'
|
||||
import showRetentionPoliciesParser from 'shared/parsing/showRetentionPolicies'
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
const DatabaseList = React.createClass({
|
||||
propTypes: {
|
||||
query: PropTypes.shape({}).isRequired,
|
||||
onChooseNamespace: PropTypes.func.isRequired,
|
||||
query: shape({}).isRequired,
|
||||
onChooseNamespace: func.isRequired,
|
||||
},
|
||||
|
||||
contextTypes: {
|
||||
source: PropTypes.shape({
|
||||
links: PropTypes.shape({
|
||||
proxy: PropTypes.string.isRequired,
|
||||
source: shape({
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
@ -59,10 +61,10 @@ const DatabaseList = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {onChooseNamespace, query} = this.props
|
||||
const {query, onChooseNamespace} = this.props
|
||||
|
||||
return (
|
||||
<div className="query-builder--column">
|
||||
<div className="query-builder--column query-builder--column-db">
|
||||
<div className="query-builder--heading">Databases</div>
|
||||
<div className="query-builder--list">
|
||||
{this.state.namespaces.map(namespace => {
|
||||
|
@ -73,7 +75,7 @@ const DatabaseList = React.createClass({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('query-builder--list-item', {
|
||||
className={classnames('query-builder--list-item', {
|
||||
active: isActive,
|
||||
})}
|
||||
key={`${database}..${retentionPolicy}`}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
import MultiSelectDropdown from 'src/shared/components/MultiSelectDropdown'
|
||||
import FunctionSelector from 'src/shared/components/FunctionSelector'
|
||||
import Dropdown from 'src/shared/components/Dropdown'
|
||||
|
||||
import {INFLUXQL_FUNCTIONS} from '../constants'
|
||||
|
@ -20,6 +20,19 @@ const FieldListItem = React.createClass({
|
|||
isKapacitorRule: bool.isRequired,
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
isOpen: false,
|
||||
}
|
||||
},
|
||||
|
||||
toggleFunctionsMenu(e) {
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
}
|
||||
this.setState({isOpen: !this.state.isOpen})
|
||||
},
|
||||
|
||||
handleToggleField() {
|
||||
this.props.onToggleField(this.props.fieldFunc)
|
||||
},
|
||||
|
@ -29,38 +42,65 @@ const FieldListItem = React.createClass({
|
|||
field: this.props.fieldFunc.field,
|
||||
funcs: this.props.isKapacitorRule ? [selectedFuncs.text] : selectedFuncs,
|
||||
})
|
||||
this.setState({isOpen: false})
|
||||
},
|
||||
|
||||
render() {
|
||||
const {isKapacitorRule, fieldFunc, isSelected} = this.props
|
||||
const {isOpen} = this.state
|
||||
const {field: fieldText} = fieldFunc
|
||||
const items = INFLUXQL_FUNCTIONS.map(text => {
|
||||
return {text}
|
||||
})
|
||||
|
||||
if (isKapacitorRule) {
|
||||
return (
|
||||
<div
|
||||
className={classnames('query-builder--list-item', {active: isSelected})}
|
||||
key={fieldFunc}
|
||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||
>
|
||||
<span>
|
||||
<div className="query-builder--checkbox" />
|
||||
{fieldText}
|
||||
</span>
|
||||
{isSelected
|
||||
? <Dropdown
|
||||
items={items}
|
||||
onChoose={this.handleApplyFunctions}
|
||||
selected={
|
||||
fieldFunc.funcs.length ? fieldFunc.funcs[0] : 'Function'
|
||||
}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('query-builder--list-item', {active: isSelected})}
|
||||
key={fieldFunc}
|
||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||
>
|
||||
<span>
|
||||
<div className="query-builder--checkbox" />
|
||||
{fieldText}
|
||||
</span>
|
||||
{isKapacitorRule
|
||||
? <Dropdown
|
||||
items={items}
|
||||
onChoose={this.handleApplyFunctions}
|
||||
selected={
|
||||
fieldFunc.funcs.length ? fieldFunc.funcs[0] : 'Function'
|
||||
}
|
||||
/>
|
||||
: <MultiSelectDropdown
|
||||
items={INFLUXQL_FUNCTIONS}
|
||||
<div key={fieldFunc}>
|
||||
<div
|
||||
className={classnames('query-builder--list-item', {active: isSelected})}
|
||||
onClick={_.wrap(fieldFunc, this.handleToggleField)}
|
||||
>
|
||||
<span>
|
||||
<div className="query-builder--checkbox" />
|
||||
{fieldText}
|
||||
</span>
|
||||
{isSelected
|
||||
? <div className={classnames('btn btn-xs btn-info', {'function-selector--toggled': isOpen})} onClick={this.toggleFunctionsMenu}>
|
||||
Functions
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
{(isSelected && isOpen)
|
||||
? <FunctionSelector
|
||||
onApply={this.handleApplyFunctions}
|
||||
selectedItems={fieldFunc.funcs || []}
|
||||
/>}
|
||||
/>
|
||||
: null}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import groupByTimeOptions from 'hson!../data/groupByTimes.hson'
|
||||
|
||||
|
@ -17,7 +17,7 @@ const GroupByTimeDropdown = React.createClass({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('dropdown group-by-time-dropdown', {
|
||||
className={classnames('dropdown group-by-time-dropdown', {
|
||||
open: isOpen,
|
||||
})}
|
||||
>
|
||||
|
|
|
@ -1,23 +1,29 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {showMeasurements} from 'shared/apis/metaQuery'
|
||||
import showMeasurementsParser from 'shared/parsing/showMeasurements'
|
||||
import TagList from './TagList'
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
const MeasurementList = React.createClass({
|
||||
propTypes: {
|
||||
query: PropTypes.shape({
|
||||
database: PropTypes.string,
|
||||
measurement: PropTypes.string,
|
||||
query: shape({
|
||||
database: string,
|
||||
measurement: string,
|
||||
}).isRequired,
|
||||
onChooseMeasurement: PropTypes.func.isRequired,
|
||||
onChooseMeasurement: func.isRequired,
|
||||
onChooseTag: func.isRequired,
|
||||
onToggleTagAcceptance: func.isRequired,
|
||||
onGroupByTag: func.isRequired,
|
||||
},
|
||||
|
||||
contextTypes: {
|
||||
source: PropTypes.shape({
|
||||
links: PropTypes.shape({
|
||||
proxy: PropTypes.string.isRequired,
|
||||
source: shape({
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
},
|
||||
|
@ -69,6 +75,11 @@ const MeasurementList = React.createClass({
|
|||
})
|
||||
},
|
||||
|
||||
handleAcceptReject(e) {
|
||||
e.stopPropagation()
|
||||
this.props.onToggleTagAcceptance()
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="query-builder--column">
|
||||
|
@ -111,15 +122,34 @@ const MeasurementList = React.createClass({
|
|||
<div className="query-builder--list">
|
||||
{measurements.map(measurement => {
|
||||
const isActive = measurement === this.props.query.measurement
|
||||
const numTagsActive = Object.keys(this.props.query.tags).length
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('query-builder--list-item', {
|
||||
active: isActive,
|
||||
})}
|
||||
key={measurement}
|
||||
onClick={_.wrap(measurement, this.props.onChooseMeasurement)}
|
||||
>
|
||||
{measurement}
|
||||
<div key={measurement}>
|
||||
<div className={classnames('query-builder--list-item', {active: isActive})}>
|
||||
<span onClick={isActive ? _.wrap(null, this.props.onChooseMeasurement) : _.wrap(measurement, this.props.onChooseMeasurement)}>
|
||||
<div className="query-builder--caret icon caret-right"></div>
|
||||
{measurement}
|
||||
</span>
|
||||
{(isActive && numTagsActive >= 1)
|
||||
? <div className={classnames('flip-toggle', {flipped: this.props.query.areTagsAccepted})} onClick={this.handleAcceptReject}>
|
||||
<div className="flip-toggle--container">
|
||||
<div className="flip-toggle--front">!=</div>
|
||||
<div className="flip-toggle--back">=</div>
|
||||
</div>
|
||||
</div>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
{
|
||||
isActive ?
|
||||
<TagList
|
||||
query={this.props.query}
|
||||
onChooseTag={this.props.onChooseTag}
|
||||
onGroupByTag={this.props.onGroupByTag}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
|
||||
import DatabaseList from './DatabaseList'
|
||||
import DatabaseDropdown from './DatabaseDropdown'
|
||||
import MeasurementList from './MeasurementList'
|
||||
import FieldList from './FieldList'
|
||||
import TagList from './TagList'
|
||||
import QueryEditor from './QueryEditor'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
|
||||
|
@ -40,6 +40,7 @@ const QueryBuilder = React.createClass({
|
|||
toggleTagAcceptance: func.isRequired,
|
||||
editRawTextAsync: func.isRequired,
|
||||
}).isRequired,
|
||||
layout: string,
|
||||
},
|
||||
|
||||
handleChooseNamespace(namespace) {
|
||||
|
@ -107,7 +108,37 @@ const QueryBuilder = React.createClass({
|
|||
},
|
||||
|
||||
renderLists() {
|
||||
const {query} = this.props
|
||||
const {query, layout} = this.props
|
||||
|
||||
// Panel layout uses a dropdown instead of a list for database selection
|
||||
// Also groups measurements & fields into their own container so they
|
||||
// can be stacked vertically.
|
||||
// TODO: Styles to make all this look proper
|
||||
if (layout === 'panel') {
|
||||
return (
|
||||
<div className="query-builder--panel">
|
||||
<DatabaseDropdown
|
||||
query={query}
|
||||
onChooseNamespace={this.handleChooseNamespace}
|
||||
/>
|
||||
<div className="query-builder">
|
||||
<MeasurementList
|
||||
query={query}
|
||||
onChooseMeasurement={this.handleChooseMeasurement}
|
||||
onChooseTag={this.handleChooseTag}
|
||||
onToggleTagAcceptance={this.handleToggleTagAcceptance}
|
||||
onGroupByTag={this.handleGroupByTag}
|
||||
/>
|
||||
<FieldList
|
||||
query={query}
|
||||
onToggleField={this.handleToggleField}
|
||||
onGroupByTime={this.handleGroupByTime}
|
||||
applyFuncsToField={this.handleApplyFuncsToField}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="query-builder">
|
||||
|
@ -118,6 +149,9 @@ const QueryBuilder = React.createClass({
|
|||
<MeasurementList
|
||||
query={query}
|
||||
onChooseMeasurement={this.handleChooseMeasurement}
|
||||
onChooseTag={this.handleChooseTag}
|
||||
onToggleTagAcceptance={this.handleToggleTagAcceptance}
|
||||
onGroupByTag={this.handleGroupByTag}
|
||||
/>
|
||||
<FieldList
|
||||
query={query}
|
||||
|
@ -125,12 +159,6 @@ const QueryBuilder = React.createClass({
|
|||
onGroupByTime={this.handleGroupByTime}
|
||||
applyFuncsToField={this.handleApplyFuncsToField}
|
||||
/>
|
||||
<TagList
|
||||
query={query}
|
||||
onChooseTag={this.handleChooseTag}
|
||||
onGroupByTag={this.handleGroupByTag}
|
||||
onToggleTagAcceptance={this.handleToggleTagAcceptance}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import _ from 'lodash'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import Dropdown from 'src/shared/components/Dropdown'
|
||||
import LoadingDots from 'src/shared/components/LoadingDots'
|
||||
|
@ -207,7 +207,7 @@ class QueryEditor extends Component {
|
|||
spellCheck="false"
|
||||
/>
|
||||
<div
|
||||
className={classNames('varmoji', {'varmoji-rotated': isTemplating})}
|
||||
className={classnames('varmoji', {'varmoji-rotated': isTemplating})}
|
||||
>
|
||||
<div className="varmoji-container">
|
||||
<div className="varmoji-front">{this.renderStatus(status)}</div>
|
||||
|
@ -259,14 +259,14 @@ class QueryEditor extends Component {
|
|||
return (
|
||||
<div className="query-editor--status">
|
||||
<span
|
||||
className={classNames('query-status-output', {
|
||||
className={classnames('query-status-output', {
|
||||
'query-status-output--error': status.error,
|
||||
'query-status-output--success': status.success,
|
||||
'query-status-output--warning': status.warn,
|
||||
})}
|
||||
>
|
||||
<span
|
||||
className={classNames('icon', {
|
||||
className={classnames('icon', {
|
||||
stop: status.error,
|
||||
checkmark: status.success,
|
||||
'alert-triangle': status.warn,
|
||||
|
|
|
@ -3,6 +3,7 @@ import React, {PropTypes} from 'react'
|
|||
import QueryBuilder from './QueryBuilder'
|
||||
import QueryMakerTab from './QueryMakerTab'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const {arrayOf, bool, func, node, number, shape, string} = PropTypes
|
||||
|
||||
|
@ -42,6 +43,7 @@ const QueryMaker = React.createClass({
|
|||
onDeleteQuery: func.isRequired,
|
||||
activeQueryIndex: number,
|
||||
children: node,
|
||||
layout: string,
|
||||
},
|
||||
|
||||
handleAddQuery() {
|
||||
|
@ -65,9 +67,9 @@ const QueryMaker = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {height, top} = this.props
|
||||
const {height, top, layout} = this.props
|
||||
return (
|
||||
<div className="query-maker" style={{height, top}}>
|
||||
<div className={classnames('query-maker', {'query-maker--panel': layout === 'panel'})} style={{height, top}}>
|
||||
{this.renderQueryTabList()}
|
||||
{this.renderQueryBuilder()}
|
||||
</div>
|
||||
|
@ -75,7 +77,7 @@ const QueryMaker = React.createClass({
|
|||
},
|
||||
|
||||
renderQueryBuilder() {
|
||||
const {timeRange, actions, source, templates, isInDataExplorer} = this.props
|
||||
const {timeRange, actions, source, templates, layout, isInDataExplorer} = this.props
|
||||
const query = this.getActiveQuery()
|
||||
|
||||
if (!query) {
|
||||
|
@ -94,6 +96,19 @@ const QueryMaker = React.createClass({
|
|||
)
|
||||
}
|
||||
|
||||
// NOTE
|
||||
// the layout prop is intended to toggle between a horizontal and vertical layout
|
||||
// the layout will be horizontal by default
|
||||
// vertical layout is known as "panel" layout as it will be used to build
|
||||
// a "cell editor panel" though that term might change
|
||||
// Currently, if set to "panel" the only noticeable difference is that the
|
||||
// DatabaseList becomes DatabaseDropdown (more space efficient in vertical layout)
|
||||
// and is outside the container with measurements/tags/fields
|
||||
//
|
||||
// TODO:
|
||||
// - perhaps switch to something like "isVertical" and accept boolean instead of string
|
||||
// - more css/markup work to make the alternate appearance look good
|
||||
|
||||
return (
|
||||
<QueryBuilder
|
||||
source={source}
|
||||
|
@ -102,6 +117,7 @@ const QueryMaker = React.createClass({
|
|||
query={query}
|
||||
actions={actions}
|
||||
onAddQuery={this.handleAddQuery}
|
||||
layout={layout}
|
||||
isInDataExplorer={isInDataExplorer}
|
||||
/>
|
||||
)
|
||||
|
@ -147,4 +163,7 @@ const QueryMaker = React.createClass({
|
|||
},
|
||||
})
|
||||
|
||||
QueryMaker.defaultProps = {
|
||||
layout: 'default',
|
||||
}
|
||||
export default QueryMaker
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const QueryMakerTab = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -25,7 +25,7 @@ const QueryMakerTab = React.createClass({
|
|||
render() {
|
||||
return (
|
||||
<div
|
||||
className={classNames('query-maker--tab', {
|
||||
className={classnames('query-maker--tab', {
|
||||
active: this.props.isActive,
|
||||
})}
|
||||
onClick={this.handleSelect}
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, {PropTypes} from 'react'
|
|||
import Dimensions from 'react-dimensions'
|
||||
import _ from 'lodash'
|
||||
import moment from 'moment'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
|
||||
|
@ -212,7 +212,7 @@ const ChronoTable = React.createClass({
|
|||
|
||||
const TabItem = ({name, index, onClickTab, isActive}) => (
|
||||
<div
|
||||
className={classNames('table--tab', {active: isActive})}
|
||||
className={classnames('table--tab', {active: isActive})}
|
||||
onClick={() => onClickTab(index)}
|
||||
>
|
||||
{name}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import _ from 'lodash'
|
||||
import cx from 'classnames'
|
||||
|
||||
import TagListItem from './TagListItem'
|
||||
|
||||
|
@ -18,7 +17,6 @@ const TagList = React.createClass({
|
|||
areTagsAccepted: bool.isRequired,
|
||||
}).isRequired,
|
||||
onChooseTag: func.isRequired,
|
||||
onToggleTagAcceptance: func.isRequired,
|
||||
onGroupByTag: func.isRequired,
|
||||
},
|
||||
|
||||
|
@ -97,57 +95,19 @@ const TagList = React.createClass({
|
|||
this._getTags()
|
||||
},
|
||||
|
||||
handleAcceptReject(e) {
|
||||
e.stopPropagation()
|
||||
this.props.onToggleTagAcceptance()
|
||||
},
|
||||
|
||||
render() {
|
||||
const {query} = this.props
|
||||
|
||||
return (
|
||||
<div className="query-builder--column">
|
||||
<div className="query-builder--heading">
|
||||
<span>Tags</span>
|
||||
{!query.database || !query.measurement || !query.retentionPolicy
|
||||
? null
|
||||
: <div
|
||||
className={cx('flip-toggle', {flipped: query.areTagsAccepted})}
|
||||
onClick={this.handleAcceptReject}
|
||||
>
|
||||
<div className="flip-toggle--container">
|
||||
<div className="flip-toggle--front">!=</div>
|
||||
<div className="flip-toggle--back">=</div>
|
||||
</div>
|
||||
</div>}
|
||||
</div>
|
||||
{this.renderList()}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
renderList() {
|
||||
const {database, measurement, retentionPolicy} = this.props.query
|
||||
if (!database || !measurement || !retentionPolicy) {
|
||||
return (
|
||||
<div className="query-builder--list-empty">
|
||||
<span>No <strong>Measurement</strong> selected</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="query-builder--list">
|
||||
<div className="query-builder--sub-list">
|
||||
{_.map(this.state.tags, (tagValues, tagKey) => {
|
||||
return (
|
||||
<TagListItem
|
||||
key={tagKey}
|
||||
tagKey={tagKey}
|
||||
tagValues={tagValues}
|
||||
selectedTagValues={this.props.query.tags[tagKey] || []}
|
||||
isUsingGroupBy={
|
||||
this.props.query.groupBy.tags.indexOf(tagKey) > -1
|
||||
}
|
||||
selectedTagValues={query.tags[tagKey] || []}
|
||||
isUsingGroupBy={query.groupBy.tags.indexOf(tagKey) > -1}
|
||||
onChooseTag={this.props.onChooseTag}
|
||||
onGroupByTag={this.props.onGroupByTag}
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import _ from 'lodash'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const {string, arrayOf, func, bool} = PropTypes
|
||||
const TagListItem = React.createClass({
|
||||
|
@ -69,7 +69,7 @@ const TagListItem = React.createClass({
|
|||
<span className="icon search" />
|
||||
</div>
|
||||
{filtered.map(v => {
|
||||
const cx = classNames('query-builder--list-item', {
|
||||
const cx = classnames('query-builder--list-item', {
|
||||
active: selectedTagValues.indexOf(v) > -1,
|
||||
})
|
||||
return (
|
||||
|
@ -98,7 +98,7 @@ const TagListItem = React.createClass({
|
|||
return (
|
||||
<div>
|
||||
<div
|
||||
className={classNames('query-builder--list-item', {active: isOpen})}
|
||||
className={classnames('query-builder--list-item', {active: isOpen})}
|
||||
onClick={this.handleClickKey}
|
||||
>
|
||||
<span>
|
||||
|
@ -106,7 +106,7 @@ const TagListItem = React.createClass({
|
|||
{tagItemLabel}
|
||||
</span>
|
||||
<div
|
||||
className={classNames('btn btn-info btn-xs group-by-tag', {
|
||||
className={classnames('btn btn-info btn-xs group-by-tag', {
|
||||
active: this.props.isUsingGroupBy,
|
||||
})}
|
||||
onClick={this.handleGroupBy}
|
||||
|
|
|
@ -1,23 +1,21 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const VisHeader = ({views, view, onToggleView, name}) => (
|
||||
<div className="graph-heading">
|
||||
<div className="graph-actions">
|
||||
{views.length
|
||||
? <ul className="toggle toggle-sm">
|
||||
{views.map(v => (
|
||||
<li
|
||||
key={v}
|
||||
onClick={() => onToggleView(v)}
|
||||
className={classNames('toggle-btn ', {active: view === v})}
|
||||
>
|
||||
{v}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
: null}
|
||||
</div>
|
||||
{views.length
|
||||
? <ul className="toggle toggle-sm">
|
||||
{views.map(v => (
|
||||
<li
|
||||
key={v}
|
||||
onClick={() => onToggleView(v)}
|
||||
className={classnames('toggle-btn ', {active: view === v})}
|
||||
>
|
||||
{v}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
: null}
|
||||
<div className="graph-title">{name}</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -24,7 +24,11 @@ const VisView = ({
|
|||
|
||||
if (view === 'table') {
|
||||
if (!query) {
|
||||
return <div className="generic-empty-state">Enter your query above</div>
|
||||
return (
|
||||
<div className="graph-empty">
|
||||
<p>Build a Query above</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import VisHeader from 'src/data_explorer/components/VisHeader'
|
||||
import VisView from 'src/data_explorer/components/VisView'
|
||||
import {GRAPH, TABLE} from 'src/shared/constants'
|
||||
|
@ -109,7 +109,7 @@ const Visualization = React.createClass({
|
|||
name={cellName}
|
||||
/>
|
||||
<div
|
||||
className={classNames({
|
||||
className={classnames({
|
||||
'graph-container': view === GRAPH,
|
||||
'table-container': view === TABLE,
|
||||
})}
|
||||
|
|
|
@ -11,6 +11,11 @@ export const INFLUXQL_FUNCTIONS = [
|
|||
'stddev',
|
||||
]
|
||||
|
||||
export const MINIMUM_HEIGHTS = {
|
||||
queryMaker: 350,
|
||||
visualization: 200,
|
||||
}
|
||||
|
||||
const SEPARATOR = 'SEPARATOR'
|
||||
|
||||
export const QUERY_TEMPLATES = [
|
||||
|
|
|
@ -7,11 +7,10 @@ import _ from 'lodash'
|
|||
import QueryMaker from '../components/QueryMaker'
|
||||
import Visualization from '../components/Visualization'
|
||||
import Header from '../containers/Header'
|
||||
import ResizeContainer, {
|
||||
ResizeBottom,
|
||||
} from 'src/shared/components/ResizeContainer'
|
||||
import ResizeContainer from 'src/shared/components/ResizeContainer'
|
||||
|
||||
import {VIS_VIEWS} from 'src/shared/constants'
|
||||
import {MINIMUM_HEIGHTS} from '../constants'
|
||||
import {setAutoRefresh} from 'shared/actions/app'
|
||||
import * as viewActions from 'src/data_explorer/actions/view'
|
||||
|
||||
|
@ -90,7 +89,11 @@ const DataExplorer = React.createClass({
|
|||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
/>
|
||||
<ResizeContainer>
|
||||
<ResizeContainer
|
||||
containerClass="page-contents"
|
||||
minTopHeight={MINIMUM_HEIGHTS.queryMaker}
|
||||
minBottomHeight={MINIMUM_HEIGHTS.visualization}
|
||||
>
|
||||
<QueryMaker
|
||||
source={source}
|
||||
queries={queryConfigs}
|
||||
|
@ -102,17 +105,15 @@ const DataExplorer = React.createClass({
|
|||
onDeleteQuery={this.handleDeleteQuery}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
/>
|
||||
<ResizeBottom>
|
||||
<Visualization
|
||||
isInDataExplorer={true}
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
queryConfigs={queryConfigs}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
editQueryStatus={queryConfigActions.editQueryStatus}
|
||||
views={VIS_VIEWS}
|
||||
/>
|
||||
</ResizeBottom>
|
||||
<Visualization
|
||||
isInDataExplorer={true}
|
||||
autoRefresh={autoRefresh}
|
||||
timeRange={timeRange}
|
||||
queryConfigs={queryConfigs}
|
||||
activeQueryIndex={activeQueryIndex}
|
||||
editQueryStatus={queryConfigActions.editQueryStatus}
|
||||
views={VIS_VIEWS}
|
||||
/>
|
||||
</ResizeContainer>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -4,7 +4,6 @@ import buildInfluxQLQuery from 'utils/influxql'
|
|||
import DatabaseList from '../../data_explorer/components/DatabaseList'
|
||||
import MeasurementList from '../../data_explorer/components/MeasurementList'
|
||||
import FieldList from '../../data_explorer/components/FieldList'
|
||||
import TagList from '../../data_explorer/components/TagList'
|
||||
|
||||
export const DataSection = React.createClass({
|
||||
propTypes: {
|
||||
|
@ -105,6 +104,9 @@ export const DataSection = React.createClass({
|
|||
<MeasurementList
|
||||
query={query}
|
||||
onChooseMeasurement={this.handleChooseMeasurement}
|
||||
onChooseTag={this.handleChooseTag}
|
||||
onGroupByTag={this.handleGroupByTag}
|
||||
onToggleTagAcceptance={this.handleToggleTagAcceptance}
|
||||
/>
|
||||
<FieldList
|
||||
query={query}
|
||||
|
@ -113,12 +115,6 @@ export const DataSection = React.createClass({
|
|||
applyFuncsToField={this.handleApplyFuncsToField}
|
||||
isKapacitorRule={true}
|
||||
/>
|
||||
<TagList
|
||||
query={query}
|
||||
onChooseTag={this.handleChooseTag}
|
||||
onGroupByTag={this.handleGroupByTag}
|
||||
onToggleTagAcceptance={this.handleToggleTagAcceptance}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
getSources,
|
||||
getKapacitors as getKapacitorsAJAX,
|
||||
updateKapacitor as updateKapacitorAJAX,
|
||||
deleteKapacitor as deleteKapacitorAJAX,
|
||||
} from 'src/shared/apis'
|
||||
import {publishNotification} from './notifications'
|
||||
|
||||
|
@ -44,6 +45,13 @@ export const setActiveKapacitor = kapacitor => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const deleteKapacitor = kapacitor => ({
|
||||
type: 'DELETE_KAPACITOR',
|
||||
payload: {
|
||||
kapacitor,
|
||||
},
|
||||
})
|
||||
|
||||
// Async action creators
|
||||
|
||||
export const removeAndLoadSources = source => async dispatch => {
|
||||
|
@ -88,3 +96,17 @@ export const setActiveKapacitorAsync = kapacitor => async dispatch => {
|
|||
const kapacitorPost = {...kapacitor, active: true}
|
||||
await updateKapacitorAJAX(kapacitorPost)
|
||||
}
|
||||
|
||||
export const deleteKapacitorAsync = kapacitor => async dispatch => {
|
||||
try {
|
||||
await deleteKapacitorAJAX(kapacitor)
|
||||
dispatch(deleteKapacitor(kapacitor))
|
||||
} catch (err) {
|
||||
dispatch(
|
||||
publishNotification(
|
||||
'error',
|
||||
'Internal Server Error. Could not delete Kapacitor config.'
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,18 @@ export const getKapacitors = async source => {
|
|||
}
|
||||
}
|
||||
|
||||
export const deleteKapacitor = async kapacitor => {
|
||||
try {
|
||||
return await AJAX({
|
||||
method: 'DELETE',
|
||||
url: kapacitor.links.self,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function createKapacitor(
|
||||
source,
|
||||
{url, name = 'My Kapacitor', username, password}
|
||||
|
@ -151,11 +163,9 @@ export function updateKapacitorConfigSection(kapacitor, section, properties) {
|
|||
}
|
||||
|
||||
export function testAlertOutput(kapacitor, outputName, properties) {
|
||||
return kapacitorProxy(
|
||||
kapacitor,
|
||||
'GET',
|
||||
'/kapacitor/v1/service-tests'
|
||||
).then(({data: {services}}) => {
|
||||
return kapacitorProxy(kapacitor, 'GET', '/kapacitor/v1/service-tests').then(({
|
||||
data: {services},
|
||||
}) => {
|
||||
const service = services.find(s => s.name === outputName)
|
||||
return kapacitorProxy(
|
||||
kapacitor,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PropTypes, Component} from 'react'
|
||||
import rome from 'rome'
|
||||
import moment from 'moment'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import OnClickOutside from 'react-onclickoutside'
|
||||
|
||||
class CustomTimeRange extends Component {
|
||||
|
@ -46,7 +46,7 @@ class CustomTimeRange extends Component {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('custom-time-range', {show: isVisible})}
|
||||
className={classnames('custom-time-range', {show: isVisible})}
|
||||
style={{display: 'flex'}}
|
||||
>
|
||||
<button
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
import {INFLUXQL_FUNCTIONS} from 'src/data_explorer/constants'
|
||||
|
||||
class FunctionSelector extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
localSelectedItems: this.props.selectedItems,
|
||||
}
|
||||
|
||||
this.onSelect = ::this.onSelect
|
||||
this.handleApplyFunctions = ::this.handleApplyFunctions
|
||||
}
|
||||
|
||||
onSelect(item, e) {
|
||||
e.stopPropagation()
|
||||
|
||||
const {localSelectedItems} = this.state
|
||||
|
||||
let nextItems
|
||||
if (this.isSelected(item)) {
|
||||
nextItems = localSelectedItems.filter(i => i !== item)
|
||||
} else {
|
||||
nextItems = [...localSelectedItems, item]
|
||||
}
|
||||
|
||||
this.setState({localSelectedItems: nextItems})
|
||||
}
|
||||
|
||||
isSelected(item) {
|
||||
return !!this.state.localSelectedItems.find(text => text === item)
|
||||
}
|
||||
|
||||
handleApplyFunctions(e) {
|
||||
e.stopPropagation()
|
||||
|
||||
this.props.onApply(this.state.localSelectedItems)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {localSelectedItems} = this.state
|
||||
|
||||
return (
|
||||
<div className="function-selector">
|
||||
<div className="function-selector--header">
|
||||
<span>
|
||||
{localSelectedItems.length > 0
|
||||
? `${localSelectedItems.length} Selected`
|
||||
: 'Select functions below'
|
||||
}
|
||||
</span>
|
||||
<div className="btn btn-xs btn-primary" onClick={this.handleApplyFunctions}>Apply</div>
|
||||
</div>
|
||||
<div className="function-selector--grid">
|
||||
{INFLUXQL_FUNCTIONS.map((f, i) => {
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={classnames('function-selector--item', {
|
||||
active: this.isSelected(f),
|
||||
})}
|
||||
onClick={_.wrap(f, this.onSelect)}
|
||||
>{f}</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, string} = PropTypes
|
||||
|
||||
FunctionSelector.propTypes = {
|
||||
onApply: func.isRequired,
|
||||
selectedItems: arrayOf(string.isRequired).isRequired,
|
||||
}
|
||||
|
||||
export default FunctionSelector
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import Dygraph from 'shared/components/Dygraph'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import shallowCompare from 'react-addons-shallow-compare'
|
||||
import _ from 'lodash'
|
||||
|
||||
|
@ -91,7 +91,7 @@ export default React.createClass({
|
|||
if (isFetchingInitially) {
|
||||
return (
|
||||
<div className="graph-fetching">
|
||||
<h3 className="graph-spinner" />
|
||||
<div className="graph-spinner" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -126,7 +126,7 @@ export default React.createClass({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('dygraph', {
|
||||
className={classnames('dygraph', {
|
||||
'graph--hasYLabel': !!(options.ylabel || options.y2label),
|
||||
})}
|
||||
style={{height: '100%'}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import OnClickOutside from 'shared/components/OnClickOutside'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import _ from 'lodash'
|
||||
|
||||
const labelText = ({localSelectedItems, isOpen, label}) => {
|
||||
|
@ -48,14 +48,14 @@ class MultiSelectDropdown extends Component {
|
|||
if (this.isSelected(item)) {
|
||||
nextItems = localSelectedItems.filter(i => i !== item)
|
||||
} else {
|
||||
nextItems = localSelectedItems.concat(item)
|
||||
nextItems = [...localSelectedItems, item]
|
||||
}
|
||||
|
||||
this.setState({localSelectedItems: nextItems})
|
||||
}
|
||||
|
||||
isSelected(item) {
|
||||
return this.state.localSelectedItems.indexOf(item) > -1
|
||||
return !!this.state.localSelectedItems.find(text => text === item)
|
||||
}
|
||||
|
||||
onApplyFunctions(e) {
|
||||
|
@ -71,7 +71,7 @@ class MultiSelectDropdown extends Component {
|
|||
|
||||
return (
|
||||
<div
|
||||
className={classNames('dropdown multi-select-dropdown', {open: isOpen})}
|
||||
className={classnames('dropdown multi-select-dropdown', {open: isOpen})}
|
||||
>
|
||||
<div
|
||||
onClick={::this.toggleMenu}
|
||||
|
@ -108,7 +108,7 @@ class MultiSelectDropdown extends Component {
|
|||
return (
|
||||
<li
|
||||
key={i}
|
||||
className={classNames('multi-select-dropdown__item', {
|
||||
className={classnames('multi-select-dropdown__item', {
|
||||
active: this.isSelected(listItem),
|
||||
})}
|
||||
onClick={_.wrap(listItem, this.onSelect)}
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import ResizeHandle from 'shared/components/ResizeHandle'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const {node, string} = PropTypes
|
||||
const {node, number, string} = PropTypes
|
||||
|
||||
const maximumNumChildren = 2
|
||||
const minimumTopHeight = 200
|
||||
const minimumBottomHeight = 200
|
||||
|
||||
const ResizeContainer = React.createClass({
|
||||
propTypes: {
|
||||
children: node.isRequired,
|
||||
containerClass: string.isRequired,
|
||||
minTopHeight: number,
|
||||
minBottomHeight: number,
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
minTopHeight: minimumTopHeight,
|
||||
minBottomHeight: minimumBottomHeight,
|
||||
}
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
|
@ -33,17 +48,18 @@ const ResizeContainer = React.createClass({
|
|||
return
|
||||
}
|
||||
|
||||
const appHeight = parseInt(
|
||||
const {minTopHeight, minBottomHeight} = this.props
|
||||
const oneHundred = 100
|
||||
const containerHeight = parseInt(
|
||||
getComputedStyle(this.refs.resizeContainer).height,
|
||||
10
|
||||
)
|
||||
// headingOffset moves the resize handle as many pixels as the page-heading is taking up.
|
||||
const headingOffset = window.innerHeight - appHeight
|
||||
const turnToPercent = 100
|
||||
// verticalOffset moves the resize handle as many pixels as the page-heading is taking up.
|
||||
const verticalOffset = window.innerHeight - containerHeight
|
||||
const newTopPanelPercent = Math.ceil(
|
||||
(e.pageY - headingOffset) / appHeight * turnToPercent
|
||||
(e.pageY - verticalOffset) / containerHeight * oneHundred
|
||||
)
|
||||
const newBottomPanelPercent = turnToPercent - newTopPanelPercent
|
||||
const newBottomPanelPercent = oneHundred - newTopPanelPercent
|
||||
|
||||
// Don't trigger a resize unless the change in size is greater than minResizePercentage
|
||||
const minResizePercentage = 0.5
|
||||
|
@ -54,15 +70,13 @@ const ResizeContainer = React.createClass({
|
|||
return
|
||||
}
|
||||
|
||||
// Don't trigger a resize if the new sizes are too small
|
||||
const minTopPanelHeight = 200
|
||||
const minBottomPanelHeight = 200
|
||||
const topHeightPixels = newTopPanelPercent / turnToPercent * appHeight
|
||||
const bottomHeightPixels = newBottomPanelPercent / turnToPercent * appHeight
|
||||
const topHeightPixels = newTopPanelPercent / oneHundred * containerHeight
|
||||
const bottomHeightPixels = newBottomPanelPercent / oneHundred * containerHeight
|
||||
|
||||
// Don't trigger a resize if the new sizes are too small
|
||||
if (
|
||||
topHeightPixels < minTopPanelHeight ||
|
||||
bottomHeightPixels < minBottomPanelHeight
|
||||
topHeightPixels < minTopHeight ||
|
||||
bottomHeightPixels < minBottomHeight
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
@ -70,8 +84,6 @@ const ResizeContainer = React.createClass({
|
|||
this.setState({
|
||||
topHeight: `${newTopPanelPercent}%`,
|
||||
bottomHeight: `${newBottomPanelPercent}%`,
|
||||
topHeightPixels,
|
||||
bottomHeightPixels,
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -87,42 +99,32 @@ const ResizeContainer = React.createClass({
|
|||
},
|
||||
|
||||
render() {
|
||||
const {topHeight, topHeightPixels, bottomHeightPixels} = this.state
|
||||
const top = React.cloneElement(this.props.children[0], {
|
||||
height: topHeight,
|
||||
heightPixels: topHeightPixels,
|
||||
})
|
||||
const bottom = React.cloneElement(this.props.children[1], {
|
||||
height: `${bottomHeightPixels}px`,
|
||||
})
|
||||
const {topHeight, bottomHeight, isDragging} = this.state
|
||||
const {containerClass, children} = this.props
|
||||
|
||||
if (React.Children.count(children) > maximumNumChildren) {
|
||||
console.error(`There cannot be more than ${maximumNumChildren}' children in ResizeContainer`)
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="resize-container page-contents"
|
||||
className={classnames(`resize--container ${containerClass}`, {'resize--dragging': isDragging})}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
onMouseUp={this.handleStopDrag}
|
||||
onMouseMove={this.handleDrag}
|
||||
ref="resizeContainer"
|
||||
>
|
||||
{top}
|
||||
<div className="resize--top" style={{height: topHeight}}>
|
||||
{React.cloneElement(children[0])}
|
||||
</div>
|
||||
{this.renderHandle()}
|
||||
{bottom}
|
||||
<div className="resize--bottom" style={{height: bottomHeight, top: topHeight}}>
|
||||
{React.cloneElement(children[1])}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
})
|
||||
|
||||
export const ResizeBottom = ({height, children}) => {
|
||||
const child = React.cloneElement(children, {height})
|
||||
return (
|
||||
<div className="resize-bottom" style={{height}}>
|
||||
{child}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
ResizeBottom.propTypes = {
|
||||
children: node.isRequired,
|
||||
height: string,
|
||||
}
|
||||
|
||||
export default ResizeContainer
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react'
|
||||
import cx from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const {func, bool, string} = React.PropTypes
|
||||
const ResizeHandle = React.createClass({
|
||||
|
@ -14,7 +14,7 @@ const ResizeHandle = React.createClass({
|
|||
|
||||
return (
|
||||
<div
|
||||
className={cx('resizer__handle', {dragging: isDragging})}
|
||||
className={classnames('resizer--handle', {dragging: isDragging})}
|
||||
onMouseDown={onHandleStartDrag}
|
||||
style={{top}}
|
||||
/>
|
||||
|
|
|
@ -20,8 +20,8 @@ export default React.createClass({
|
|||
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
|
||||
if (this.props.isFetchingInitially) {
|
||||
return (
|
||||
<div className="graph-panel__graph-fetching">
|
||||
<h3 className="graph-panel__spinner" />
|
||||
<div className="graph-empty">
|
||||
<h3 className="graph-spinner" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import cx from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const {node, func, bool, number, string} = PropTypes
|
||||
export const Tab = React.createClass({
|
||||
|
@ -13,7 +13,7 @@ export const Tab = React.createClass({
|
|||
render() {
|
||||
return (
|
||||
<div
|
||||
className={cx('btn tab', {active: this.props.isActive})}
|
||||
className={classnames('btn tab', {active: this.props.isActive})}
|
||||
onClick={this.props.isDisabled ? null : this.props.onClick}
|
||||
>
|
||||
{this.props.children}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import OnClickOutside from 'react-onclickoutside'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const TemplateDrawer = ({
|
||||
templates,
|
||||
|
@ -11,7 +11,7 @@ const TemplateDrawer = ({
|
|||
<div className="template-drawer">
|
||||
{templates.map(t => (
|
||||
<div
|
||||
className={classNames('template-drawer--item', {
|
||||
className={classnames('template-drawer--item', {
|
||||
'template-drawer--selected': t.tempVar === selected.tempVar,
|
||||
})}
|
||||
onMouseOver={() => {
|
||||
|
|
|
@ -54,6 +54,18 @@ const sourcesReducer = (state = initialState, action) => {
|
|||
})
|
||||
return updatedSources
|
||||
}
|
||||
|
||||
case 'DELETE_KAPACITOR': {
|
||||
const {kapacitor} = action.payload
|
||||
const updatedSources = _.cloneDeep(state)
|
||||
updatedSources.forEach(source => {
|
||||
const index = _.findIndex(source.kapacitors, k => k.id === kapacitor.id)
|
||||
if (index >= 0) {
|
||||
source.kapacitors.splice(index, 1)
|
||||
}
|
||||
})
|
||||
return updatedSources
|
||||
}
|
||||
}
|
||||
|
||||
return state
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
import cx from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
|
||||
const {node, string} = PropTypes
|
||||
|
||||
|
@ -16,7 +16,7 @@ const NavListItem = React.createClass({
|
|||
const isActive = location.startsWith(link)
|
||||
|
||||
return (
|
||||
<Link className={cx('sidebar__menu-item', {active: isActive})} to={link}>
|
||||
<Link className={classnames('sidebar__menu-item', {active: isActive})} to={link}>
|
||||
{children}
|
||||
</Link>
|
||||
)
|
||||
|
@ -63,7 +63,7 @@ const NavBlock = React.createClass({
|
|||
})
|
||||
|
||||
return (
|
||||
<div className={cx('sidebar__square', className, {active: isActive})}>
|
||||
<div className={classnames('sidebar__square', className, {active: isActive})}>
|
||||
{this.renderLink()}
|
||||
<div className={wrapperClassName || 'sidebar__menu-wrapper'}>
|
||||
<div className="sidebar__menu">
|
||||
|
|
|
@ -3,7 +3,13 @@ import {Link, withRouter} from 'react-router'
|
|||
|
||||
import Dropdown from 'shared/components/Dropdown'
|
||||
|
||||
const kapacitorDropdown = (kapacitors, source, router, setActiveKapacitor) => {
|
||||
const kapacitorDropdown = (
|
||||
kapacitors,
|
||||
source,
|
||||
router,
|
||||
setActiveKapacitor,
|
||||
handleDeleteKapacitor
|
||||
) => {
|
||||
if (!kapacitors || kapacitors.length === 0) {
|
||||
return (
|
||||
<Link to={`/sources/${source.id}/kapacitors/new`}>Add Kapacitor</Link>
|
||||
|
@ -45,6 +51,14 @@ const kapacitorDropdown = (kapacitors, source, router, setActiveKapacitor) => {
|
|||
router.push(`${item.resource}/edit`)
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: 'trash',
|
||||
text: 'delete',
|
||||
handler: item => {
|
||||
handleDeleteKapacitor(item.kapacitor)
|
||||
},
|
||||
confirmable: true,
|
||||
},
|
||||
]}
|
||||
selected={selected}
|
||||
/>
|
||||
|
@ -58,6 +72,7 @@ const InfluxTable = ({
|
|||
location,
|
||||
router,
|
||||
setActiveKapacitor,
|
||||
handleDeleteKapacitor,
|
||||
}) => (
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
|
@ -100,7 +115,8 @@ const InfluxTable = ({
|
|||
s.kapacitors,
|
||||
s,
|
||||
router,
|
||||
setActiveKapacitor
|
||||
setActiveKapacitor,
|
||||
handleDeleteKapacitor
|
||||
)}
|
||||
</td>
|
||||
<td className="text-right">
|
||||
|
@ -147,6 +163,7 @@ InfluxTable.propTypes = {
|
|||
}),
|
||||
sources: array.isRequired,
|
||||
setActiveKapacitor: func.isRequired,
|
||||
handleDeleteKapacitor: func.isRequired,
|
||||
}
|
||||
|
||||
export default withRouter(InfluxTable)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import classNames from 'classnames'
|
||||
import classnames from 'classnames'
|
||||
import {insecureSkipVerifyText} from 'src/shared/copy/tooltipText'
|
||||
import _ from 'lodash'
|
||||
|
||||
|
@ -166,7 +166,7 @@ export const SourceForm = React.createClass({
|
|||
: null}
|
||||
<div className="form-group form-group-submit col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button
|
||||
className={classNames('btn btn-block', {
|
||||
className={classnames('btn btn-block', {
|
||||
'btn-primary': editMode,
|
||||
'btn-success': !editMode,
|
||||
})}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
removeAndLoadSources,
|
||||
fetchKapacitorsAsync,
|
||||
setActiveKapacitorAsync,
|
||||
deleteKapacitorAsync,
|
||||
} from 'src/shared/actions/sources'
|
||||
|
||||
import InfluxTable from '../components/InfluxTable'
|
||||
|
@ -45,7 +46,7 @@ class ManageSources extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {sources, source, setActiveKapacitor} = this.props
|
||||
const {sources, source, setActiveKapacitor, deleteKapacitor} = this.props
|
||||
|
||||
return (
|
||||
<div className="page" id="manage-sources-page">
|
||||
|
@ -63,6 +64,7 @@ class ManageSources extends Component {
|
|||
source={source}
|
||||
sources={sources}
|
||||
setActiveKapacitor={setActiveKapacitor}
|
||||
handleDeleteKapacitor={deleteKapacitor}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -86,6 +88,7 @@ ManageSources.propTypes = {
|
|||
removeAndLoadSources: func.isRequired,
|
||||
fetchKapacitors: func.isRequired,
|
||||
setActiveKapacitor: func.isRequired,
|
||||
deleteKapacitor: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({sources}) => ({
|
||||
|
@ -96,6 +99,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
removeAndLoadSources: bindActionCreators(removeAndLoadSources, dispatch),
|
||||
fetchKapacitors: bindActionCreators(fetchKapacitorsAsync, dispatch),
|
||||
setActiveKapacitor: bindActionCreators(setActiveKapacitorAsync, dispatch),
|
||||
deleteKapacitor: bindActionCreators(deleteKapacitorAsync, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ManageSources)
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
@import 'components/search-widget';
|
||||
@import 'components/source-indicator';
|
||||
@import 'components/tables';
|
||||
@import 'components/function-selector';
|
||||
|
||||
// Pages
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
Function Selector
|
||||
------------------------------------------------------
|
||||
Used within the FieldListItem component
|
||||
*/
|
||||
$function-selector--bg: $g2-kevlar;
|
||||
$function-selector--gutter: 6px;
|
||||
$function-selector--size: 26px;
|
||||
$function-selector--item: $g3-castle;
|
||||
$function-selector--text: $g11-sidewalk;
|
||||
$function-selector--item-hover: $g4-onyx;
|
||||
$function-selector--text-hover: $g15-platinum;
|
||||
$function-selector--item-active: $c-pool;
|
||||
$function-selector--text-active: $g20-white;
|
||||
|
||||
.function-selector {
|
||||
padding: 4px 11px 8px 32px;
|
||||
background-color: $g4-onyx;
|
||||
}
|
||||
.function-selector--header {
|
||||
background-color: $function-selector--bg;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: $function-selector--gutter;
|
||||
border-radius: $radius $radius 0 0;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: $g11-sidewalk;
|
||||
@include no-user-select();
|
||||
}
|
||||
.function-selector--grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: $function-selector--gutter;
|
||||
padding-top: 0;
|
||||
border-radius: 0 0 $radius $radius;
|
||||
background-color: $function-selector--bg;
|
||||
}
|
||||
.function-selector--item {
|
||||
@include no-user-select();
|
||||
border-radius: $radius;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
font-family: $code-font;
|
||||
flex: 0 0 calc(25% - 2px);
|
||||
margin: 1px;
|
||||
text-align: center;
|
||||
height: $function-selector--size;
|
||||
line-height: ($function-selector--size - 3px);
|
||||
background-color: $function-selector--item;
|
||||
color: $function-selector--text;
|
||||
transition:
|
||||
background-color 0.25s ease,
|
||||
color 0.25s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: $function-selector--item-hover;
|
||||
color: $function-selector--text-hover;
|
||||
cursor: pointer;
|
||||
}
|
||||
&.active {
|
||||
background-color: $function-selector--item-active;
|
||||
color: $function-selector--text-active;
|
||||
}
|
||||
}
|
||||
|
||||
.btn.function-selector--toggled {
|
||||
&, &:hover, &:active, &:hover:active {
|
||||
background-color: $g7-graphite !important;
|
||||
color: $g20-white !important;
|
||||
}
|
||||
}
|
|
@ -3,33 +3,35 @@
|
|||
---------------------------------------------
|
||||
*/
|
||||
$graph-heading-height: 44px;
|
||||
|
||||
$graph-gutter: 16px;
|
||||
|
||||
/*
|
||||
Graph Styles
|
||||
---------------------------------------------
|
||||
*/
|
||||
.graph,
|
||||
.graph-heading,
|
||||
.graph-container,
|
||||
.graph > .table-container {
|
||||
position: relative;
|
||||
}
|
||||
.graph {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: calc(100% - #{($page-wrapper-padding * 2)});
|
||||
left: $page-wrapper-padding;
|
||||
margin: 0 $page-wrapper-padding;
|
||||
height: 100%;
|
||||
}
|
||||
.graph-heading {
|
||||
position: relative;
|
||||
top: 16px;
|
||||
background-color: $g3-castle;
|
||||
border-radius: $radius $radius 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: $graph-heading-height;
|
||||
top: $graph-gutter;
|
||||
padding: 0 16px;
|
||||
transition:
|
||||
background-color 0.25s ease;
|
||||
|
||||
.toggle {
|
||||
margin: 0;
|
||||
.toggle-btn {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
|
@ -37,22 +39,19 @@ $graph-heading-height: 44px;
|
|||
font-size: 14px;
|
||||
color: $g13-mist;
|
||||
font-weight: 600;
|
||||
margin-right: 16px;
|
||||
@include no-user-select();
|
||||
transition:
|
||||
color 0.25s ease;
|
||||
}
|
||||
.graph-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.graph .table-container {
|
||||
.graph .table-container,
|
||||
.graph-container {
|
||||
top: $graph-gutter;
|
||||
background-color: $g3-castle;
|
||||
border-radius: 0 0 $radius $radius;
|
||||
height: calc(100% - #{($graph-heading-height + ($graph-gutter * 2))});
|
||||
}
|
||||
.graph .table-container {
|
||||
padding: 8px 16px;
|
||||
position: relative;
|
||||
top: 16px;
|
||||
height: calc(100% - #{($graph-heading-height + 32px)});
|
||||
|
||||
& > div > div:last-child {
|
||||
position: absolute;
|
||||
|
@ -63,26 +62,10 @@ $graph-heading-height: 44px;
|
|||
.fixedDataTableLayout_main {
|
||||
height: 100% !important;
|
||||
}
|
||||
.generic-empty-state {
|
||||
background-color: transparent;
|
||||
padding: 50px 0;
|
||||
height: 100%;
|
||||
font-size: 22px;
|
||||
@include no-user-select();
|
||||
}
|
||||
}
|
||||
.graph-container {
|
||||
@include no-user-select();
|
||||
background-color: $g3-castle;
|
||||
border-radius: 0 0 $radius $radius;
|
||||
position: relative;
|
||||
height: 316px;
|
||||
transition:
|
||||
background-color 0.25s ease;
|
||||
top: 16px;
|
||||
height: calc(100% - #{$graph-heading-height + 32px});
|
||||
}
|
||||
.data-explorer .graph-container {
|
||||
|
||||
& > div:not(.graph-panel__refreshing) {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
@ -101,15 +84,16 @@ $graph-heading-height: 44px;
|
|||
|
||||
.graph-empty {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
@include no-user-select();
|
||||
|
||||
> p {
|
||||
font-size: 28px;
|
||||
font-weight: 300;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: $g8-storm;
|
||||
|
|
|
@ -9,12 +9,13 @@
|
|||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
overflow-x: auto;
|
||||
@include custom-scrollbar($query-builder--list-bg,$c-pool);
|
||||
}
|
||||
.query-builder--column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 2 0 0;
|
||||
}
|
||||
.query-builder--column-db {
|
||||
flex: 1 0 0;
|
||||
}
|
||||
.query-builder--heading {
|
||||
|
@ -200,4 +201,10 @@
|
|||
.query-builder--list-item:hover .group-by-tag,
|
||||
.query-builder--list-item.active .group-by-tag {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.query-builder--db-dropdown {
|
||||
display: inline-block;
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
border-radius: 0 $radius 0 0;
|
||||
background-color: $query-editor--bg;
|
||||
position: relative;
|
||||
z-index: 2; /* Minimum amount to obcure the toggle flip within Query Builder. Will fix later */
|
||||
z-index: 3; /* Minimum amount to obcure the toggle flip within Query Builder. Will fix later */
|
||||
}
|
||||
.query-editor--field {
|
||||
font-family: $code-font;
|
||||
|
|
|
@ -7,9 +7,8 @@
|
|||
*/
|
||||
|
||||
.query-maker {
|
||||
position: relative;
|
||||
left: $explorer-page-padding;
|
||||
width: calc(100% - #{($explorer-page-padding * 2)});
|
||||
height: 100%;
|
||||
margin: 0 $explorer-page-padding;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
/*
|
||||
Resizable Container
|
||||
----------------------------------------------
|
||||
*/
|
||||
|
||||
$resizer-line-width: 2px;
|
||||
$resizer-line-z: 2;
|
||||
$resizer-handle-width: 10px;
|
||||
|
@ -9,14 +14,40 @@ $resizer-color: $g5-pepper;
|
|||
$resizer-color-hover: $g8-storm;
|
||||
$resizer-color-active: $c-pool;
|
||||
|
||||
.resizer__handle {
|
||||
.resize--container {
|
||||
overflow: hidden !important;
|
||||
|
||||
&.resize--dragging * {
|
||||
@include no-user-select();
|
||||
}
|
||||
}
|
||||
.resize--top,
|
||||
.resize--bottom {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.resizer--full-size {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
Resizable Container Handle
|
||||
----------------------------------------------
|
||||
*/
|
||||
.resizer--handle {
|
||||
top: 60%;
|
||||
left: 0;
|
||||
height: $resizer-click-area;
|
||||
margin-top: -$resizer-click-area/2;
|
||||
margin-bottom: -$resizer-click-area/2;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
z-index: 1;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
position: absolute;
|
||||
|
@ -78,37 +109,4 @@ $resizer-color-active: $c-pool;
|
|||
box-shadow: 0 0 $resizer-glow $resizer-color-active;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.resize-container.page-contents {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.resize-container {
|
||||
.resize-top {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 60%;
|
||||
width: 100%;
|
||||
}
|
||||
.resize-bottom {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
height: 40%;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Special rule for when a graph is in the bottom of resizer */
|
||||
.resize-bottom .graph {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,10 @@ $overlay-z: 100;
|
|||
z-index: $overlay-z;
|
||||
padding: 0 30px;
|
||||
|
||||
/*
|
||||
Semi-transparent gradient in background
|
||||
Makes it possible to leave opacity alone
|
||||
*/
|
||||
&:before {
|
||||
content: '';
|
||||
display: block;
|
||||
|
@ -37,7 +41,6 @@ $overlay-z: 100;
|
|||
flex: 0 0 $overlay-controls-height;
|
||||
width: calc(100% - #{($explorer-page-padding * 2)});
|
||||
left: $explorer-page-padding;
|
||||
margin-top: 16px;
|
||||
border: 0;
|
||||
background-color: $g2-kevlar;
|
||||
border-radius: $radius $radius 0 0;
|
||||
|
@ -65,6 +68,13 @@ $overlay-z: 100;
|
|||
@include no-user-select;
|
||||
}
|
||||
}
|
||||
.overlay-technology--editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
padding: 16px 0;
|
||||
}
|
||||
.overlay-controls .confirm-buttons {
|
||||
margin-left: 32px;
|
||||
}
|
||||
|
@ -92,30 +102,12 @@ $overlay-z: 100;
|
|||
}
|
||||
|
||||
/* Graph editing in Dashboards is a little smaller so the dash can be seen in the background */
|
||||
.overlay-technology .resize-container.page-contents {
|
||||
background-image: none !important;
|
||||
overflow: visible;
|
||||
}
|
||||
.overlay-technology .graph {
|
||||
width: 70%;
|
||||
left: 15%;
|
||||
}
|
||||
.overlay-technology .graph-heading,
|
||||
.overlay-technology .graph-container,
|
||||
.overlay-technology .table-container {
|
||||
top: -24px;
|
||||
}
|
||||
.overlay-technology .graph-heading .graph-actions {
|
||||
order: 2;
|
||||
}
|
||||
.overlay-technology .graph-container,
|
||||
.overlay-technology .table-container {
|
||||
height: calc(100% - 38px);
|
||||
margin: 0 15%;
|
||||
}
|
||||
.overlay-technology .query-maker {
|
||||
flex: 1 0 0;
|
||||
padding: 0 8px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 0 0 $radius $radius;
|
||||
background-color: $g2-kevlar;
|
||||
}
|
||||
|
|
|
@ -348,6 +348,7 @@ $toggle-border: 2px;
|
|||
|
||||
.toggle {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
width: auto;
|
||||
padding: $toggle-border;
|
||||
border-radius: 3px;
|
||||
|
|
Loading…
Reference in New Issue