Merge pull request #1858 from influxdata/feature/grooveknob
Refactor DisplayOptions UX to provide affirmative choice for 'auto', via a new componentpull/10616/head
commit
0632370417
|
@ -31,6 +31,7 @@
|
|||
1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Assign a series consistent coloring when it appears in multiple cells
|
||||
1. [#1800](https://github.com/influxdata/chronograf/pull/1800): Increase size of line protocol manual entry in Data Explorer's Write Data overlay
|
||||
1. [#1812](https://github.com/influxdata/chronograf/pull/1812): Improve error message when request for Status Page News Feed fails
|
||||
1. [#1858](https://github.com/influxdata/chronograf/pull/1858): Provide affirmative UI choice for 'auto' in DisplayOptions with new toggle-based component
|
||||
|
||||
## v1.3.5.0 [2017-07-27]
|
||||
### Bug Fixes
|
||||
|
|
|
@ -1,11 +1,19 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import OptIn from 'shared/components/OptIn'
|
||||
|
||||
// TODO: add logic for for Prefix, Suffix, Scale, and Multiplier
|
||||
const AxesOptions = ({onSetRange, onSetLabel, axes}) => {
|
||||
const AxesOptions = ({
|
||||
onSetYAxisBoundMin,
|
||||
onSetYAxisBoundMax,
|
||||
onSetLabel,
|
||||
axes,
|
||||
}) => {
|
||||
const min = _.get(axes, ['y', 'bounds', '0'], '')
|
||||
const max = _.get(axes, ['y', 'bounds', '1'], '')
|
||||
const label = _.get(axes, ['y', 'label'], '')
|
||||
const defaultYLabel = _.get(axes, ['y', 'defaultYLabel'], '')
|
||||
|
||||
return (
|
||||
<div className="display-options--cell">
|
||||
|
@ -13,43 +21,31 @@ const AxesOptions = ({onSetRange, onSetLabel, axes}) => {
|
|||
<form autoComplete="off" style={{margin: '0 -6px'}}>
|
||||
<div className="form-group col-sm-12">
|
||||
<label htmlFor="prefix">Title</label>
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
<OptIn
|
||||
customPlaceholder={defaultYLabel}
|
||||
customValue={label}
|
||||
onSetValue={onSetLabel}
|
||||
type="text"
|
||||
name="label"
|
||||
id="label"
|
||||
value={label}
|
||||
onChange={onSetLabel}
|
||||
placeholder="auto"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-sm-6">
|
||||
<label htmlFor="min">Min</label>
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
<OptIn
|
||||
customPlaceholder={'min'}
|
||||
customValue={min}
|
||||
onSetValue={onSetYAxisBoundMin}
|
||||
type="number"
|
||||
name="min"
|
||||
id="min"
|
||||
value={min}
|
||||
onChange={onSetRange}
|
||||
placeholder="auto"
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-sm-6">
|
||||
<label htmlFor="max">Max</label>
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
<OptIn
|
||||
customPlaceholder={'max'}
|
||||
customValue={max}
|
||||
onSetValue={onSetYAxisBoundMax}
|
||||
type="number"
|
||||
name="max"
|
||||
id="max"
|
||||
value={max}
|
||||
onChange={onSetRange}
|
||||
placeholder="auto"
|
||||
/>
|
||||
</div>
|
||||
<p className="display-options--footnote">
|
||||
Values left blank will be set automatically
|
||||
</p>
|
||||
{/* <div className="form-group col-sm-6">
|
||||
<label htmlFor="prefix">Labels Prefix</label>
|
||||
<input
|
||||
|
@ -90,12 +86,14 @@ const AxesOptions = ({onSetRange, onSetLabel, axes}) => {
|
|||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
AxesOptions.propTypes = {
|
||||
onSetRange: func.isRequired,
|
||||
onSetYAxisBoundMin: func.isRequired,
|
||||
onSetYAxisBoundMax: func.isRequired,
|
||||
onSetLabel: func.isRequired,
|
||||
axes: shape({
|
||||
y: shape({
|
||||
bounds: arrayOf(string),
|
||||
label: string,
|
||||
defaultYLabel: string,
|
||||
}),
|
||||
}).isRequired,
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@ import defaultQueryConfig from 'src/utils/defaultQueryConfig'
|
|||
import buildInfluxQLQuery from 'utils/influxql'
|
||||
import {getQueryConfig} from 'shared/apis'
|
||||
|
||||
import {buildYLabel} from 'shared/presenters'
|
||||
import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
|
||||
import {OVERLAY_TECHNOLOGY} from 'shared/constants/classNames'
|
||||
import {MINIMUM_HEIGHTS, INITIAL_HEIGHTS} from 'src/data_explorer/constants'
|
||||
|
@ -32,7 +31,8 @@ class CellEditorOverlay extends Component {
|
|||
this.handleClickDisplayOptionsTab = ::this.handleClickDisplayOptionsTab
|
||||
this.handleSetActiveQueryIndex = ::this.handleSetActiveQueryIndex
|
||||
this.handleEditRawText = ::this.handleEditRawText
|
||||
this.handleSetYAxisBounds = ::this.handleSetYAxisBounds
|
||||
this.handleSetYAxisBoundMin = ::this.handleSetYAxisBoundMin
|
||||
this.handleSetYAxisBoundMax = ::this.handleSetYAxisBoundMax
|
||||
this.handleSetLabel = ::this.handleSetLabel
|
||||
this.getActiveQuery = ::this.getActiveQuery
|
||||
|
||||
|
@ -48,25 +48,10 @@ class CellEditorOverlay extends Component {
|
|||
queriesWorkingDraft,
|
||||
activeQueryIndex: 0,
|
||||
isDisplayOptionsTabActive: false,
|
||||
axes: this.setDefaultLabels(axes, queries),
|
||||
axes,
|
||||
}
|
||||
}
|
||||
|
||||
setDefaultLabels(axes, queries) {
|
||||
if (!queries.length) {
|
||||
return axes
|
||||
}
|
||||
|
||||
if (axes.y.label) {
|
||||
return axes
|
||||
}
|
||||
|
||||
const q = queries[0].queryConfig
|
||||
const label = buildYLabel(q)
|
||||
|
||||
return {...axes, y: {...axes.y, label}}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {status, queryID} = this.props.queryStatus
|
||||
const nextStatus = nextProps.queryStatus
|
||||
|
@ -94,22 +79,28 @@ class CellEditorOverlay extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleSetYAxisBounds(e) {
|
||||
const {min, max} = e.target.form
|
||||
handleSetYAxisBoundMin(min) {
|
||||
const {axes} = this.state
|
||||
const {y: {bounds: [, max]}} = axes
|
||||
|
||||
this.setState({
|
||||
axes: {...axes, y: {...axes.y, bounds: [min.value, max.value]}},
|
||||
axes: {...axes, y: {...axes.y, bounds: [min, max]}},
|
||||
})
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
handleSetLabel(e) {
|
||||
const {label} = e.target.form
|
||||
handleSetYAxisBoundMax(max) {
|
||||
const {axes} = this.state
|
||||
const {y: {bounds: [min]}} = axes
|
||||
|
||||
this.setState({
|
||||
axes: {...axes, y: {...axes.y, bounds: [min, max]}},
|
||||
})
|
||||
}
|
||||
|
||||
handleSetLabel(label) {
|
||||
const {axes} = this.state
|
||||
|
||||
this.setState({axes: {...axes, y: {...axes.y, label: label.value}}})
|
||||
e.preventDefault()
|
||||
this.setState({axes: {...axes, y: {...axes.y, label}}})
|
||||
}
|
||||
|
||||
handleAddQuery() {
|
||||
|
@ -258,9 +249,11 @@ class CellEditorOverlay extends Component {
|
|||
? <DisplayOptions
|
||||
selectedGraphType={cellWorkingType}
|
||||
onSelectGraphType={this.handleSelectGraphType}
|
||||
onSetRange={this.handleSetYAxisBounds}
|
||||
onSetYAxisBoundMin={this.handleSetYAxisBoundMin}
|
||||
onSetYAxisBoundMax={this.handleSetYAxisBoundMax}
|
||||
onSetLabel={this.handleSetLabel}
|
||||
axes={axes}
|
||||
queryConfigs={queriesWorkingDraft}
|
||||
/>
|
||||
: <QueryMaker
|
||||
source={source}
|
||||
|
|
|
@ -1,31 +1,72 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import GraphTypeSelector from 'src/dashboards/components/GraphTypeSelector'
|
||||
import AxesOptions from 'src/dashboards/components/AxesOptions'
|
||||
|
||||
const DisplayOptions = ({
|
||||
selectedGraphType,
|
||||
onSelectGraphType,
|
||||
onSetLabel,
|
||||
onSetRange,
|
||||
axes,
|
||||
}) =>
|
||||
<div className="display-options">
|
||||
<GraphTypeSelector
|
||||
selectedGraphType={selectedGraphType}
|
||||
onSelectGraphType={onSelectGraphType}
|
||||
/>
|
||||
<AxesOptions onSetLabel={onSetLabel} onSetRange={onSetRange} axes={axes} />
|
||||
</div>
|
||||
import {buildDefaultYLabel} from 'shared/presenters'
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
class DisplayOptions extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const {axes, queryConfigs} = props
|
||||
|
||||
this.state = {
|
||||
axes: this.setDefaultLabels(axes, queryConfigs),
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {axes, queryConfigs} = nextProps
|
||||
|
||||
this.setState({axes: this.setDefaultLabels(axes, queryConfigs)})
|
||||
}
|
||||
|
||||
setDefaultLabels(axes, queryConfigs) {
|
||||
return queryConfigs.length
|
||||
? {
|
||||
...axes,
|
||||
y: {...axes.y, defaultYLabel: buildDefaultYLabel(queryConfigs[0])},
|
||||
}
|
||||
: axes
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
selectedGraphType,
|
||||
onSelectGraphType,
|
||||
onSetLabel,
|
||||
onSetYAxisBoundMin,
|
||||
onSetYAxisBoundMax,
|
||||
} = this.props
|
||||
const {axes} = this.state
|
||||
|
||||
return (
|
||||
<div className="display-options">
|
||||
<GraphTypeSelector
|
||||
selectedGraphType={selectedGraphType}
|
||||
onSelectGraphType={onSelectGraphType}
|
||||
/>
|
||||
<AxesOptions
|
||||
onSetLabel={onSetLabel}
|
||||
onSetYAxisBoundMin={onSetYAxisBoundMin}
|
||||
onSetYAxisBoundMax={onSetYAxisBoundMax}
|
||||
axes={axes}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
DisplayOptions.propTypes = {
|
||||
selectedGraphType: string.isRequired,
|
||||
onSelectGraphType: func.isRequired,
|
||||
onSetRange: func.isRequired,
|
||||
onSetYAxisBoundMin: func.isRequired,
|
||||
onSetYAxisBoundMax: func.isRequired,
|
||||
onSetLabel: func.isRequired,
|
||||
axes: shape({}).isRequired,
|
||||
queryConfigs: arrayOf(shape()).isRequired,
|
||||
}
|
||||
|
||||
export default DisplayOptions
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
import onClickOutside from 'shared/components/OnClickOutside'
|
||||
|
||||
class ClickOutsideInput extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.handleClickOutside = ::this.handleClickOutside
|
||||
}
|
||||
|
||||
handleClickOutside(e) {
|
||||
this.props.handleClickOutsideCustomValueInput(e)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
id,
|
||||
type,
|
||||
customPlaceholder,
|
||||
onGetRef,
|
||||
customValue,
|
||||
onFocus,
|
||||
onChange,
|
||||
onKeyDown,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
id={id}
|
||||
type={type}
|
||||
name={customPlaceholder}
|
||||
ref={onGetRef}
|
||||
value={customValue}
|
||||
onFocus={onFocus}
|
||||
onChange={onChange}
|
||||
onKeyDown={onKeyDown}
|
||||
placeholder={customPlaceholder}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, string} = PropTypes
|
||||
|
||||
ClickOutsideInput.propTypes = {
|
||||
id: string.isRequired,
|
||||
type: string.isRequired,
|
||||
customPlaceholder: string.isRequired,
|
||||
customValue: string.isRequired,
|
||||
onGetRef: func.isRequired,
|
||||
onFocus: func.isRequired,
|
||||
onChange: func.isRequired,
|
||||
onKeyDown: func.isRequired,
|
||||
handleClickOutsideCustomValueInput: func.isRequired,
|
||||
}
|
||||
|
||||
export default onClickOutside(ClickOutsideInput)
|
|
@ -9,7 +9,7 @@ import getRange from 'shared/parsing/getRangeForDygraph'
|
|||
|
||||
import {LINE_COLORS, multiColumnBarPlotter} from 'src/shared/graphs/helpers'
|
||||
import DygraphLegend from 'src/shared/components/DygraphLegend'
|
||||
import {buildYLabel} from 'shared/presenters'
|
||||
import {buildDefaultYLabel} from 'shared/presenters'
|
||||
|
||||
const hasherino = (str, len) =>
|
||||
str
|
||||
|
@ -68,7 +68,7 @@ export default class Dygraph extends Component {
|
|||
return label
|
||||
}
|
||||
|
||||
return buildYLabel(queryConfig)
|
||||
return buildDefaultYLabel(queryConfig)
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import uuid from 'node-uuid'
|
||||
|
||||
import ClickOutsideInput from 'shared/components/ClickOutsideInput'
|
||||
|
||||
class OptIn extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
const {customValue, fixedValue} = props
|
||||
|
||||
this.state = {
|
||||
useCustomValue: customValue !== '',
|
||||
fixedValue,
|
||||
customValue,
|
||||
}
|
||||
|
||||
this.id = uuid.v4()
|
||||
this.isCustomValueInputFocused = false
|
||||
|
||||
this.useFixedValue = ::this.useFixedValue
|
||||
this.useCustomValue = ::this.useCustomValue
|
||||
this.considerResetCustomValue = ::this.considerResetCustomValue
|
||||
this.setCustomValue = ::this.setCustomValue
|
||||
this.setValue = ::this.setValue
|
||||
}
|
||||
|
||||
useFixedValue() {
|
||||
this.setState({useCustomValue: false, customValue: ''}, () =>
|
||||
this.setValue()
|
||||
)
|
||||
}
|
||||
|
||||
useCustomValue() {
|
||||
this.setState({useCustomValue: true}, () => this.setValue())
|
||||
}
|
||||
|
||||
handleClickFixedValueField() {
|
||||
return () => this.useFixedValue()
|
||||
}
|
||||
|
||||
handleClickToggle() {
|
||||
return () => {
|
||||
const useCustomValueNext = !this.state.useCustomValue
|
||||
if (useCustomValueNext) {
|
||||
this.useCustomValue()
|
||||
this.customValueInput.focus()
|
||||
} else {
|
||||
this.useFixedValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleFocusCustomValueInput() {
|
||||
return () => {
|
||||
this.isCustomValueInputFocused = true
|
||||
this.useCustomValue()
|
||||
}
|
||||
}
|
||||
|
||||
handleChangeCustomValue() {
|
||||
return e => {
|
||||
this.setCustomValue(e.target.value)
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDownCustomValueInput() {
|
||||
return e => {
|
||||
if (e.key === 'Enter' || e.key === 'Tab') {
|
||||
if (e.key === 'Enter') {
|
||||
this.customValueInput.blur()
|
||||
}
|
||||
this.considerResetCustomValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleClickOutsideCustomValueInput() {
|
||||
return e => {
|
||||
if (
|
||||
e.target.id !== this.grooveKnob.id &&
|
||||
e.target.id !== this.grooveKnobContainer.id &&
|
||||
this.isCustomValueInputFocused
|
||||
) {
|
||||
this.considerResetCustomValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
considerResetCustomValue() {
|
||||
const customValue = this.customValueInput.value.trim()
|
||||
|
||||
this.setState({customValue})
|
||||
|
||||
if (customValue === '') {
|
||||
this.useFixedValue()
|
||||
}
|
||||
|
||||
this.isCustomValueInputFocused = false
|
||||
}
|
||||
|
||||
setCustomValue(value) {
|
||||
this.setState({customValue: value}, this.setValue)
|
||||
}
|
||||
|
||||
setValue() {
|
||||
const {onSetValue} = this.props
|
||||
const {useCustomValue, fixedValue, customValue} = this.state
|
||||
|
||||
if (useCustomValue) {
|
||||
onSetValue(customValue)
|
||||
} else {
|
||||
onSetValue(fixedValue)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {fixedPlaceholder, customPlaceholder, type} = this.props
|
||||
const {useCustomValue, customValue} = this.state
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames('opt-in', {
|
||||
'right-toggled': useCustomValue,
|
||||
})}
|
||||
>
|
||||
<ClickOutsideInput
|
||||
id={this.id}
|
||||
type={type}
|
||||
customPlaceholder={customPlaceholder}
|
||||
customValue={customValue}
|
||||
onGetRef={el => (this.customValueInput = el)}
|
||||
onFocus={this.handleFocusCustomValueInput()}
|
||||
onChange={this.handleChangeCustomValue()}
|
||||
onKeyDown={this.handleKeyDownCustomValueInput()}
|
||||
handleClickOutsideCustomValueInput={this.handleClickOutsideCustomValueInput()}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="opt-in--groove-knob-container"
|
||||
id={this.id}
|
||||
ref={el => (this.grooveKnobContainer = el)}
|
||||
onClick={this.handleClickToggle()}
|
||||
>
|
||||
<div
|
||||
className="opt-in--groove-knob"
|
||||
id={this.id}
|
||||
ref={el => (this.grooveKnob = el)}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="opt-in--left-label"
|
||||
onClick={this.handleClickFixedValueField()}
|
||||
>
|
||||
{fixedPlaceholder}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
OptIn.defaultProps = {
|
||||
fixedPlaceholder: 'auto',
|
||||
fixedValue: '',
|
||||
customPlaceholder: 'Custom Value',
|
||||
customValue: '',
|
||||
}
|
||||
|
||||
const {func, oneOf, string} = PropTypes
|
||||
|
||||
OptIn.propTypes = {
|
||||
fixedPlaceholder: string,
|
||||
fixedValue: string,
|
||||
customPlaceholder: string,
|
||||
customValue: string,
|
||||
onSetValue: func.isRequired,
|
||||
type: oneOf(['text', 'number']),
|
||||
}
|
||||
|
||||
export default OptIn
|
|
@ -109,7 +109,7 @@ function getRolesForUser(roles, user) {
|
|||
return buildRoles(userRoles)
|
||||
}
|
||||
|
||||
export const buildYLabel = queryConfig => {
|
||||
export const buildDefaultYLabel = queryConfig => {
|
||||
return queryConfig.rawText
|
||||
? ''
|
||||
: `${queryConfig.measurement}.${queryConfig.fields[0].field}`
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
@import 'components/graph';
|
||||
@import 'components/input-tag-list';
|
||||
@import 'components/newsfeed';
|
||||
@import 'components/opt-in';
|
||||
@import 'components/page-header-dropdown';
|
||||
@import 'components/page-header-editable';
|
||||
@import 'components/page-spinner';
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
Opt In Component
|
||||
------------------------------------------------------------------------------
|
||||
User can toggle between a single value or any value
|
||||
*/
|
||||
.opt-in {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.opt-in--left-label {
|
||||
border: 2px solid $g5-pepper;
|
||||
border-left: 0;
|
||||
background-color: $g2-kevlar;
|
||||
color: $c-pool;
|
||||
font-family: $code-font;
|
||||
padding-right: 11px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
line-height: 24px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: default;
|
||||
@include no-user-select();
|
||||
transition: background-color 0.25s ease, color 0.25s ease;
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
.opt-in--groove-knob-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 2px solid $g5-pepper;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
position: relative;
|
||||
|
||||
// Groove
|
||||
> div.opt-in--groove-knob {
|
||||
margin: 0 10px;
|
||||
z-index: 3;
|
||||
width: 28px;
|
||||
height: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: $g6-smoke;
|
||||
position: relative;
|
||||
|
||||
// Knob
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background-color: $c-pool;
|
||||
transition: background-color 0.25s ease, transform 0.25s ease;
|
||||
transform: translate(0%, -50%);
|
||||
}
|
||||
}
|
||||
|
||||
// Background Gradients
|
||||
&:before,
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
// Left
|
||||
&:before {
|
||||
background-color: $g2-kevlar;
|
||||
z-index: 2;
|
||||
opacity: 1;
|
||||
}
|
||||
// Right
|
||||
&:after {
|
||||
@include gradient-h($g2-kevlar,$g3-castle);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
> div:after {
|
||||
background-color: $c-laser;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Customize form input
|
||||
.opt-in > input.form-control {
|
||||
border-radius: 4px 0 0 4px;
|
||||
font-family: $code-font;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
// Right value toggled state
|
||||
.opt-in.right-toggled {
|
||||
.opt-in--groove-knob:after {
|
||||
transform: translate(-100%, -50%);
|
||||
}
|
||||
// Fade out left, fade in right
|
||||
.opt-in--groove-knob-container:before {
|
||||
opacity: 0;
|
||||
}
|
||||
// Make left label look disabled
|
||||
.opt-in--left-label {
|
||||
background-color: $g3-castle;
|
||||
color: $g8-storm;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue