diff --git a/src/components/fields/FunctionSpecField.jsx b/src/components/fields/FunctionSpecField.jsx index a6920442..b0c0afc6 100644 --- a/src/components/fields/FunctionSpecField.jsx +++ b/src/components/fields/FunctionSpecField.jsx @@ -22,7 +22,7 @@ function isDataField(value) { * properties accept arrays as values, for example `text-font`. So we must try * and create an expression. */ -function isExpression(value, fieldSpec={}) { +function checkIsExpression (value, fieldSpec={}) { if (!Array.isArray(value)) { return false; } @@ -72,6 +72,13 @@ export default class FunctionSpecProperty extends React.Component { ]), } + constructor (props) { + super(); + this.state = { + isExpression: checkIsExpression(props.value, props.fieldSpec), + } + } + getFieldFunctionType(fieldSpec) { if (fieldSpec.expression.interpolated) { return "exponential" @@ -103,6 +110,13 @@ export default class FunctionSpecProperty extends React.Component { this.props.onChange(this.props.fieldName, changedValue) } + deleteExpression = (newValue) => { + this.props.onChange(this.props.fieldName, newValue); + this.setState({ + isExpression: false, + }); + } + deleteStop = (stopIdx) => { const stops = this.props.value.stops.slice(0) stops.splice(stopIdx, 1) @@ -132,6 +146,10 @@ export default class FunctionSpecProperty extends React.Component { makeExpression = () => { const expression = ["literal", this.props.value || this.props.fieldSpec.default]; this.props.onChange(this.props.fieldName, expression); + + this.setState({ + isExpression: true, + }); } makeDataFunction = () => { @@ -152,11 +170,12 @@ export default class FunctionSpecProperty extends React.Component { const propClass = this.props.fieldSpec.default === this.props.value ? "maputnik-default-property" : "maputnik-modified-property" let specField; - if (isExpression(this.props.value, this.props.fieldSpec)) { + if (this.state.isExpression) { specField = ( { - try { - const jsonVal = JSON.parse(value); - - if (isLiteralExpression(jsonVal)) { - this.setState({ - lastValue: jsonVal - }); - } - - this.props.onChange(jsonVal); - } - catch (err) { - // TODO: Handle JSON parse error + onChange = (jsonVal) => { + if (isLiteralExpression(jsonVal)) { + this.setState({ + lastValue: jsonVal + }); } + + this.props.onChange(jsonVal); } onDelete = () => { @@ -50,13 +44,13 @@ export default class ExpressionProperty extends React.Component { const {value, fieldName, fieldSpec} = this.props; if (isLiteralExpression(value)) { - this.props.onChange(value[1]); + this.props.onDelete(value[1]); } else if (isLiteralExpression(lastValue)) { - this.props.onChange(lastValue[1]); + this.props.onDelete(lastValue[1]); } else { - this.props.onChange(fieldSpec.default); + this.props.onDelete(fieldSpec.default); } } @@ -75,12 +69,16 @@ export default class ExpressionProperty extends React.Component { doc={this.props.fieldSpec.doc} label={labelFromFieldName(this.props.fieldName)} action={deleteStopBtn} + wideMode={true} > - stringifyPretty(data, {indent: 2, maxLength: 50})} + onChange={this.onChange} /> } diff --git a/src/components/inputs/InputBlock.jsx b/src/components/inputs/InputBlock.jsx index 9cfaacff..f700e463 100644 --- a/src/components/inputs/InputBlock.jsx +++ b/src/components/inputs/InputBlock.jsx @@ -28,6 +28,7 @@ class InputBlock extends React.Component { data-wd-key={this.props["data-wd-key"]} className={classnames({ "maputnik-input-block": true, + "maputnik-input-block--wide": this.props.wideMode, "maputnik-action-block": this.props.action })} > diff --git a/src/components/layers/JSONEditor.jsx b/src/components/layers/JSONEditor.jsx index e0a99ec9..eec03431 100644 --- a/src/components/layers/JSONEditor.jsx +++ b/src/components/layers/JSONEditor.jsx @@ -1,5 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' +import classnames from 'classnames'; import InputBlock from '../inputs/InputBlock' import StringInput from '../inputs/StringInput' @@ -22,6 +23,15 @@ class JSONEditor extends React.Component { layer: PropTypes.object.isRequired, maxHeight: PropTypes.number, onChange: PropTypes.func, + lineNumbers: PropTypes.bool, + } + + static defaultProps = { + lineNumbers: true, + gutters: ["CodeMirror-lint-markers"], + getValue: (data) => { + return JSON.stringify(data, null, 2) + } } constructor(props) { @@ -33,7 +43,7 @@ class JSONEditor extends React.Component { } getValue () { - return JSON.stringify(this.props.layer, null, 2); + return this.props.getValue(this.props.layer); } componentDidMount () { @@ -43,13 +53,14 @@ class JSONEditor extends React.Component { name: "javascript", json: true }, + lineWrapping: this.props.lineWrapping, tabSize: 2, theme: 'maputnik', viewportMargin: Infinity, - lineNumbers: true, + lineNumbers: this.props.lineNumbers, lint: true, matchBrackets: true, - gutters: ["CodeMirror-lint-markers"], + gutters: this.props.gutters, scrollbarStyle: "null", }); @@ -113,7 +124,7 @@ class JSONEditor extends React.Component { } return
this._el = el} style={style} /> diff --git a/src/styles/index.scss b/src/styles/index.scss index 019857a9..54c11c4b 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -35,6 +35,7 @@ height: 14px; } +// TODO: Move these into correct *.scss files .maputnik-inline-error { color: #a4a4a4; padding: 0.4em 0.4em; @@ -43,3 +44,22 @@ border-radius: 2px; margin: $margin-2 0px; } + +.maputnik-expression-editor { + border: solid 1px $color-gray; +} + +.maputnik-input-block--wide { + .maputnik-input-block-content { + display: block; + width: auto; + } + + .maputnik-input-block-label { + width: 82%; + } + + .maputnik-input-block-action { + text-align: right; + } +}