diff --git a/web/src/scripts/components/input/NumberInput.jsx b/web/src/scripts/components/input/NumberInput.jsx
index 0c34ac78..e63ea815 100644
--- a/web/src/scripts/components/input/NumberInput.jsx
+++ b/web/src/scripts/components/input/NumberInput.jsx
@@ -16,11 +16,13 @@
*/
import _ from 'lodash'
-import React, { Component, } from 'react'
+import React, { Component, PropTypes } from 'react'
import ReactDOM from 'react-dom'
import { StylesEnhancer } from 'react-styles-provider'
import pureRender from 'pure-render-decorator'
+import { getLockedValue } from '../../utils/NumberUtils'
+
const stylesCreator = ({input}, {type, width, disabled}) => ({
input: {
...(type === 'platform' ? input.platform : input.regular),
@@ -36,10 +38,13 @@ const stylesCreator = ({input}, {type, width, disabled}) => ({
export default class NumberInput extends Component {
static propTypes = {
- onChange: React.PropTypes.func.isRequired,
- onSubmit: React.PropTypes.func,
- value: React.PropTypes.number.isRequired,
- disabled: React.PropTypes.bool,
+ onChange: PropTypes.func.isRequired,
+ onSubmit: PropTypes.func,
+ value: PropTypes.number.isRequired,
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.number,
+ disabled: PropTypes.bool,
}
static defaultProps = {
@@ -89,23 +94,28 @@ export default class NumberInput extends Component {
}
onKeyDown = (e) => {
+ const {step, max, min} = this.props
let stopPropagation = false
let incrementBy = 0
switch (e.keyCode) {
+ // Tab
case 9:
;
break
+ // Enter
case 13:
stopPropagation = true
this.props.onSubmit(e.target.value)
break
+ // Up arrow
case 38:
- incrementBy = 1
+ incrementBy = step || 1
stopPropagation = true
break
+ // Down arrow
case 40:
- incrementBy = -1
+ incrementBy = step ? step * -1 : -1
stopPropagation = true
break
default:
@@ -142,6 +152,7 @@ export default class NumberInput extends Component {
}
value = this.roundInput(value)
+ value = getLockedValue(value, min, max, step)
this.props.onChange(value)
diff --git a/web/src/scripts/components/input/SliderInput.jsx b/web/src/scripts/components/input/SliderInput.jsx
index 0059a43b..4216f64e 100644
--- a/web/src/scripts/components/input/SliderInput.jsx
+++ b/web/src/scripts/components/input/SliderInput.jsx
@@ -16,10 +16,12 @@
*/
import _ from 'lodash'
-import React, { Component, } from 'react'
+import React, { Component, PropTypes } from 'react'
import { StylesEnhancer } from 'react-styles-provider'
import pureRender from 'pure-render-decorator'
+import { getLockedValue } from '../../utils/NumberUtils'
+
let SLIDER_REF = 'slider'
const stylesCreator = ({input, colors}, {type, width, height, trackHeight, disabled, knobWidth}) => {
@@ -86,17 +88,19 @@ const stylesCreator = ({input, colors}, {type, width, height, trackHeight, disab
export default class SliderInput extends Component {
static propTypes = {
- value: React.PropTypes.number,
- min: React.PropTypes.number,
- max: React.PropTypes.number,
- onChange: React.PropTypes.func,
- disabled: React.PropTypes.bool,
+ value: PropTypes.number,
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.number,
+ onChange: PropTypes.func,
+ disabled: PropTypes.bool,
}
static defaultProps = {
value: 0,
min: 0,
max: 100,
+ step: 1,
height: 30,
trackHeight: 3,
knobWidth: 12,
@@ -168,7 +172,7 @@ export default class SliderInput extends Component {
calculateValueFromPosition(position, bounds) {
const trackWidth = this.getTrackWidth(bounds)
- const {min, max, knobWidth} = this.props
+ const {min, max, knobWidth, step} = this.props
// Prevent division by 0
if (trackWidth === 0) {
@@ -180,7 +184,7 @@ export default class SliderInput extends Component {
const percent = (position - bounds.min) / trackWidth
const range = max - min
const value = (percent * range) + min
- return Math.round(_.clamp(value, min, max))
+ return getLockedValue(value, min, max, step)
}
getPercentValue() {
diff --git a/web/src/scripts/components/inspector/Property.jsx b/web/src/scripts/components/inspector/Property.jsx
index e4d12c1c..527a982d 100644
--- a/web/src/scripts/components/inspector/Property.jsx
+++ b/web/src/scripts/components/inspector/Property.jsx
@@ -58,7 +58,7 @@ export default class Property extends Component {
const {name, value, type, editWith} = prop
const inputProps = {
- value,
+ ...prop,
title: name,
onChange: this.onChange,
actions,
diff --git a/web/src/scripts/components/inspector/PropertyNumberInput.jsx b/web/src/scripts/components/inspector/PropertyNumberInput.jsx
index 55f1c80c..48384ae0 100644
--- a/web/src/scripts/components/inspector/PropertyNumberInput.jsx
+++ b/web/src/scripts/components/inspector/PropertyNumberInput.jsx
@@ -18,6 +18,7 @@
import React, { Component } from 'react'
import { StylesEnhancer } from 'react-styles-provider'
import pureRender from 'pure-render-decorator'
+import _ from 'lodash'
import PropertyField from './PropertyField'
import PropertyDivider from './PropertyDivider'
@@ -37,6 +38,8 @@ const stylesCreator = ({fonts}) => ({
}
})
+const inputPropKeys = ['value', 'min', 'max', 'step', 'onChange', 'disabled']
+
@StylesEnhancer(stylesCreator)
@pureRender
export default class PropertyNumberInput extends Component {
@@ -44,10 +47,15 @@ export default class PropertyNumberInput extends Component {
static defaultProps = {
title: '',
value: 0,
+ min: 0,
+ max: 100,
+ step: 1,
}
render() {
- const {styles, title, value, onChange, actions, dividerType, disabled} = this.props
+ const {styles, title, actions, dividerType} = this.props
+
+ const inputProps = _.pick(this.props, inputPropKeys)
return (
diff --git a/web/src/scripts/containers/ComponentInspector.jsx b/web/src/scripts/containers/ComponentInspector.jsx
index 444de7ca..afd88d84 100644
--- a/web/src/scripts/containers/ComponentInspector.jsx
+++ b/web/src/scripts/containers/ComponentInspector.jsx
@@ -25,6 +25,8 @@ import { StylesEnhancer } from 'react-styles-provider'
import { elementTreeActions } from '../actions'
import * as uiActions from '../actions/uiActions'
+import * as tabActions from '../actions/tabActions'
+import * as URIUtils from '../utils/URIUtils'
import * as selectors from '../selectors'
import { ComponentMenuItem, PaneHeader } from '../components'
import ComponentProps from './ComponentProps'
@@ -48,15 +50,18 @@ const mapStateToProps = (state) => createSelector(
selectors.selectedElement,
selectors.selectedComponent,
selectors.focusedFileId,
- (element, component, focusedFileId) => ({
+ selectors.tabContainerId,
+ (element, component, focusedFileId, tabContainerId) => ({
component: component || element,
focusedFileId,
+ tabContainerId,
})
)
const mapDispatchToProps = (dispatch) => ({
elementTreeActions: bindActionCreators(elementTreeActions, dispatch),
uiActions: bindActionCreators(uiActions, dispatch),
+ tabActions: bindActionCreators(tabActions, dispatch),
})
@StylesEnhancer(stylesCreator, ({style}) => ({style}))
@@ -69,6 +74,15 @@ class ComponentInspector extends Component {
uiActions.setSidebarContext()
}
+ onTitleClick = (component) => {
+ // TODO: if we're not in dev mode or with correct permissions, return instead
+ const {tabActions, tabContainerId} = this.props
+ const uri = URIUtils.componentIdToURI(component.id)
+
+ this.onBack()
+ tabActions.addTab(tabContainerId, uri)
+ }
+
render() {
const {style, styles, width, component, elementTreeActions} = this.props
@@ -80,6 +94,7 @@ class ComponentInspector extends Component {
/>
{component && (
diff --git a/web/src/scripts/utils/NumberUtils.js b/web/src/scripts/utils/NumberUtils.js
new file mode 100644
index 00000000..d9bc2de6
--- /dev/null
+++ b/web/src/scripts/utils/NumberUtils.js
@@ -0,0 +1,45 @@
+/**
+ * Copyright (C) 2015 Deco Software Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+export const getNearestValue = (val, low, high) => {
+ return Math.abs(val - low) < Math.abs(val - high) ? low : high
+}
+
+export const getDecimalCount = (num) => {
+ const decSplitArr = num.toString().split('.')
+ return decSplitArr[1] ? decSplitArr[1].length : 0
+}
+
+export const conditionalClamp = (val, min, max) => {
+ if (min && val <= min) {
+ return min
+ }
+ if (max && val >= max) {
+ return max
+ }
+ return val
+}
+
+export const getLockedValue = (val, min, max, step) => {
+ const stepCount = val / step
+ const nearestLower = Math.max(Math.floor(stepCount) * step, min)
+ const nearestUpper = Math.min(Math.ceil(stepCount) * step, max)
+ // If step=.1, value=.82, this gets us .8
+ const newValue = getNearestValue(val, nearestLower, nearestUpper)
+ // Clip value to step's decimals, and then remove trailing 0s
+ return Number(conditionalClamp(newValue, min, max).toFixed(getDecimalCount(step)))
+}