From b539644b2bea3be7296422d04b4f73a12e7b74cb Mon Sep 17 00:00:00 2001 From: orangemug Date: Sun, 9 Feb 2020 15:08:24 +0000 Subject: [PATCH] Added expression support for filters. --- src/components/App.jsx | 2 +- src/components/fields/FunctionSpecField.jsx | 27 +- src/components/fields/PropertyGroup.jsx | 2 +- src/components/fields/_ExpressionProperty.jsx | 62 ++--- src/components/filter/FilterEditor.jsx | 254 ++++++++++++++---- src/components/layers/JSONEditor.jsx | 5 +- src/styles/_filtereditor.scss | 5 + 7 files changed, 248 insertions(+), 109 deletions(-) diff --git a/src/components/App.jsx b/src/components/App.jsx index 32e7624c..bb9f9ec9 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -318,7 +318,7 @@ export default class App extends React.Component { onStyleChanged = (newStyle, save=true) => { - const errors = validate(newStyle, latest) + const errors = validate(newStyle, latest) || []; const mappedErrors = errors.map(error => { const layerMatch = error.message.match(/layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/); if (layerMatch) { diff --git a/src/components/fields/FunctionSpecField.jsx b/src/components/fields/FunctionSpecField.jsx index eb8c1d22..72e48171 100644 --- a/src/components/fields/FunctionSpecField.jsx +++ b/src/components/fields/FunctionSpecField.jsx @@ -8,6 +8,9 @@ import ExpressionProperty from './_ExpressionProperty' import {expression} from '@mapbox/mapbox-gl-style-spec' +function isLiteralExpression (value) { + return (Array.isArray(value) && value.length === 2 && value[0] === "literal"); +} function isZoomField(value) { return typeof value === 'object' && value.stops && typeof value.property === 'undefined' @@ -22,6 +25,7 @@ function isDataField(value) { * properties accept arrays as values, for example `text-font`. So we must try * and create an expression. */ +// TODO: Use function from filter checks. function checkIsExpression (value, fieldSpec={}) { if (!Array.isArray(value)) { return false; @@ -111,8 +115,9 @@ export default class FunctionSpecProperty extends React.Component { this.props.onChange(this.props.fieldName, changedValue) } - deleteExpression = (newValue) => { - this.props.onChange(this.props.fieldName, newValue); + deleteExpression = () => { + const {fieldSpec, fieldName} = this.props; + this.props.onChange(fieldName, fieldSpec.default); this.setState({ isExpression: false, }); @@ -144,6 +149,22 @@ export default class FunctionSpecProperty extends React.Component { this.props.onChange(this.props.fieldName, zoomFunc) } + undoExpression = () => { + const {value, fieldName} = this.props; + + if (isLiteralExpression(value)) { + this.props.onChange(fieldName, value[1]); + this.setState({ + isExpression: false + }); + } + } + + canUndo = () => { + const {value} = this.props; + return isLiteralExpression(value); + } + makeExpression = () => { const expression = ["literal", this.props.value || this.props.fieldSpec.default]; this.props.onChange(this.props.fieldName, expression); @@ -176,6 +197,8 @@ export default class FunctionSpecProperty extends React.Component { { diff --git a/src/components/fields/_ExpressionProperty.jsx b/src/components/fields/_ExpressionProperty.jsx index 8f489b12..e79ed665 100644 --- a/src/components/fields/_ExpressionProperty.jsx +++ b/src/components/fields/_ExpressionProperty.jsx @@ -11,70 +11,38 @@ import stringifyPretty from 'json-stringify-pretty-compact' import JSONEditor from '../layers/JSONEditor' -function isLiteralExpression (value) { - return (Array.isArray(value) && value.length === 2 && value[0] === "literal"); -} - export default class ExpressionProperty extends React.Component { static propTypes = { onDelete: PropTypes.func, fieldName: PropTypes.string, fieldSpec: PropTypes.object, - value: PropTypes.object, + value: PropTypes.any, error: PropTypes.object, onChange: PropTypes.func, } constructor (props) { super(); - this.state = { - lastValue: props.value, - }; - } - - onChange = (jsonVal) => { - if (isLiteralExpression(jsonVal)) { - this.setState({ - lastValue: jsonVal - }); - } - - this.props.onChange(jsonVal); - } - - onReset = () => { - const {lastValue} = this.state; - const {value, fieldSpec} = this.props; - - if (isLiteralExpression(value)) { - this.props.onDelete(value[1]); - } - else if (isLiteralExpression(lastValue)) { - this.props.onDelete(lastValue[1]); - } - } - - onDelete = () => { - const {fieldSpec} = this.props; - this.props.onDelete(fieldSpec.default); } render() { - const {lastValue} = this.state; - const {value} = this.props; + const {value, canUndo} = this.props; - const canUndo = isLiteralExpression(value) || isLiteralExpression(lastValue); const deleteStopBtn = ( <> + {this.props.onUndo && + + } - + + } + else if (isSimpleFilter) { + const filter = this.combiningFilter(); + let combiningOp = filter[0]; + let filters = filter.slice(1) + + const actions = ( +
+ +
+ ); + + const editorBlocks = filters.map((f, idx) => { + const error = errors[`filter[${idx+1}]`]; + + return ( + <> + + + + {error && +
{error.message}
+ } + + ); + }) - const editorBlocks = filters.map((f, idx) => { - const error = errors[`filter[${idx+1}]`]; return ( <> - - + - - {error && -
{error.message}
- } + + {editorBlocks} +
+ +
+
+ +
); - }) - - //TODO: Implement support for nested filter - if(hasNestedCombiningFilter(filter)) { - return
- Nested filters are not supported. -
} + else { + let {filter} = this.props; - return
-
- k.match(/filter(\[\d+\])?/)) + .map(([k, v]) => { + return v.message; + }) + .join("\n") + const error = errorMessage ? {message: errorMessage} : null; + + return ( + { + this.setState({isSimpleFilter: true}); + this.props.onChange(defaultFilter); + }} + fieldName="filter-compound-filter" fieldSpec={fieldSpec} + value={filter} + error={error} + onChange={this.props.onChange} /> - -
- {editorBlocks} -
- -
-
- -
-
+ ); + } } } diff --git a/src/components/layers/JSONEditor.jsx b/src/components/layers/JSONEditor.jsx index fe212c23..393eb618 100644 --- a/src/components/layers/JSONEditor.jsx +++ b/src/components/layers/JSONEditor.jsx @@ -13,6 +13,7 @@ import 'codemirror/lib/codemirror.css' import 'codemirror/addon/lint/lint.css' import '../../codemirror-maputnik.css' import jsonlint from 'jsonlint' +import stringifyPretty from 'json-stringify-pretty-compact' // This is mainly because of this issue also the API has changed, see comment in file import '../../vendor/codemirror/addon/lint/json-lint' @@ -20,7 +21,7 @@ import '../../vendor/codemirror/addon/lint/json-lint' class JSONEditor extends React.Component { static propTypes = { - layer: PropTypes.object.isRequired, + layer: PropTypes.any.isRequired, maxHeight: PropTypes.number, onChange: PropTypes.func, lineNumbers: PropTypes.bool, @@ -35,7 +36,7 @@ class JSONEditor extends React.Component { lineWrapping: false, gutters: ["CodeMirror-lint-markers"], getValue: (data) => { - return JSON.stringify(data, null, 2) + return stringifyPretty(data, {indent: 2, maxLength: 50} ); } } diff --git a/src/styles/_filtereditor.scss b/src/styles/_filtereditor.scss index 7c3d1542..d836be9e 100644 --- a/src/styles/_filtereditor.scss +++ b/src/styles/_filtereditor.scss @@ -1,5 +1,10 @@ .maputnik-filter-editor-wrapper { padding: $margin-3; + overflow: hidden; + + .maputnik-input-block { + margin: 0; + } } .maputnik-filter-editor {