Added support for identity functions.

This commit is contained in:
orangemug 2020-04-12 16:25:32 +01:00
parent 8f722c59de
commit 7cfe0563bc
3 changed files with 176 additions and 106 deletions

View file

@ -6,12 +6,21 @@ import DataProperty from './_DataProperty'
import ZoomProperty from './_ZoomProperty' import ZoomProperty from './_ZoomProperty'
import ExpressionProperty from './_ExpressionProperty' import ExpressionProperty from './_ExpressionProperty'
import {function as styleFunction} from '@mapbox/mapbox-gl-style-spec'; import {function as styleFunction} from '@mapbox/mapbox-gl-style-spec';
import {findDefaultFromSpec} from '../util/spec-helper';
function isLiteralExpression (value) { function isLiteralExpression (value) {
return (Array.isArray(value) && value.length === 2 && value[0] === "literal"); return (Array.isArray(value) && value.length === 2 && value[0] === "literal");
} }
function isGetExpression (value) {
return (
Array.isArray(value) &&
value.length === 2 &&
value[0] === "get"
);
}
function isZoomField(value) { function isZoomField(value) {
return ( return (
typeof(value) === 'object' && typeof(value) === 'object' &&
@ -28,7 +37,15 @@ function isZoomField(value) {
); );
} }
function isDataField(value) { function isIdentityProperty (value) {
return (
typeof(value) === 'object' &&
value.type === "identity" &&
value.hasOwnProperty("property")
);
}
function isDataStopProperty (value) {
return ( return (
typeof(value) === 'object' && typeof(value) === 'object' &&
value.stops && value.stops &&
@ -45,6 +62,13 @@ function isDataField(value) {
); );
} }
function isDataField(value) {
return (
isIdentityProperty(value) ||
isDataStopProperty(value)
);
}
function isPrimative (value) { function isPrimative (value) {
const valid = ["string", "boolean", "number"]; const valid = ["string", "boolean", "number"];
return valid.includes(typeof(value)); return valid.includes(typeof(value));
@ -78,24 +102,6 @@ function getDataType (value, fieldSpec={}) {
} }
} }
/**
* If we don't have a default value just make one up
*/
function findDefaultFromSpec (spec) {
if (spec.hasOwnProperty('default')) {
return spec.default;
}
const defaults = {
'color': '#000000',
'string': '',
'boolean': false,
'number': 0,
'array': [],
}
return defaults[spec.type] || '';
}
/** Supports displaying spec field for zoom function objects /** Supports displaying spec field for zoom function objects
* https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property * https://www.mapbox.com/mapbox-gl-style-spec/#types-function-zoom-property
@ -206,7 +212,16 @@ export default class FunctionSpecProperty extends React.Component {
undoExpression = () => { undoExpression = () => {
const {value, fieldName} = this.props; const {value, fieldName} = this.props;
if (isLiteralExpression(value)) { if (isGetExpression(value)) {
this.props.onChange(fieldName, {
"type": "identity",
"property": value[1]
});
this.setState({
dataType: "value",
});
}
else if (isLiteralExpression(value)) {
this.props.onChange(fieldName, value[1]); this.props.onChange(fieldName, value[1]);
this.setState({ this.setState({
dataType: "value", dataType: "value",
@ -217,6 +232,7 @@ export default class FunctionSpecProperty extends React.Component {
canUndo = () => { canUndo = () => {
const {value, fieldSpec} = this.props; const {value, fieldSpec} = this.props;
return ( return (
isGetExpression(value) ||
isLiteralExpression(value) || isLiteralExpression(value) ||
isPrimative(value) || isPrimative(value) ||
(Array.isArray(value) && fieldSpec.type === "array") (Array.isArray(value) && fieldSpec.type === "array")
@ -230,6 +246,9 @@ export default class FunctionSpecProperty extends React.Component {
if (typeof(value) === "object" && 'stops' in value) { if (typeof(value) === "object" && 'stops' in value) {
expression = styleFunction.convertFunction(value, fieldSpec); expression = styleFunction.convertFunction(value, fieldSpec);
} }
else if (isIdentityProperty(value)) {
expression = ["get", value.property];
}
else { else {
expression = ["literal", value || this.props.fieldSpec.default]; expression = ["literal", value || this.props.fieldSpec.default];
} }

View file

@ -10,6 +10,7 @@ import DocLabel from './DocLabel'
import InputBlock from '../inputs/InputBlock' import InputBlock from '../inputs/InputBlock'
import docUid from '../../libs/document-uid' import docUid from '../../libs/document-uid'
import sortNumerically from '../../libs/sort-numerically' import sortNumerically from '../../libs/sort-numerically'
import {findDefaultFromSpec} from '../util/spec-helper';
import labelFromFieldName from './_labelFromFieldName' import labelFromFieldName from './_labelFromFieldName'
import DeleteStopButton from './_DeleteStopButton' import DeleteStopButton from './_DeleteStopButton'
@ -89,10 +90,10 @@ export default class DataProperty extends React.Component {
getDataFunctionTypes(fieldSpec) { getDataFunctionTypes(fieldSpec) {
if (fieldSpec.expression.interpolated) { if (fieldSpec.expression.interpolated) {
return ["categorical", "interval", "exponential"] return ["categorical", "interval", "exponential", "identity"]
} }
else { else {
return ["categorical", "interval"] return ["categorical", "interval", "identity"]
} }
} }
@ -122,6 +123,29 @@ export default class DataProperty extends React.Component {
return mappedWithRef.map((item) => item.data); return mappedWithRef.map((item) => item.data);
} }
onChange = (fieldName, value) => {
if (value.type === "identity") {
value = {
type: value.type,
property: value.property,
};
}
else {
const stopValue = value.type === 'categorical' ? '' : 0;
value = {
property: "",
type: value.type,
// Default props if they don't already exist.
stops: [
[{zoom: 6, value: stopValue}, findDefaultFromSpec(this.props.fieldSpec)],
[{zoom: 10, value: stopValue}, findDefaultFromSpec(this.props.fieldSpec)]
],
...value,
}
}
this.props.onChange(fieldName, value);
}
changeStop(changeIdx, stopData, value) { changeStop(changeIdx, stopData, value) {
const stops = this.props.value.stops.slice(0) const stops = this.props.value.stops.slice(0)
const changedStop = stopData.zoom === undefined ? stopData.value : stopData const changedStop = stopData.zoom === undefined ? stopData.value : stopData
@ -133,7 +157,7 @@ export default class DataProperty extends React.Component {
...this.props.value, ...this.props.value,
stops: orderedStops, stops: orderedStops,
} }
this.props.onChange(this.props.fieldName, changedValue) this.onChange(this.props.fieldName, changedValue)
} }
changeDataProperty(propName, propVal) { changeDataProperty(propName, propVal) {
@ -143,7 +167,7 @@ export default class DataProperty extends React.Component {
else { else {
delete this.props.value[propName] delete this.props.value[propName]
} }
this.props.onChange(this.props.fieldName, this.props.value) this.onChange(this.props.fieldName, this.props.value)
} }
render() { render() {
@ -153,7 +177,9 @@ export default class DataProperty extends React.Component {
this.props.value.type = this.getFieldFunctionType(this.props.fieldSpec) this.props.value.type = this.getFieldFunctionType(this.props.fieldSpec)
} }
const dataFields = this.props.value.stops.map((stop, idx) => { let dataFields;
if (this.props.value.stops) {
dataFields = this.props.value.stops.map((stop, idx) => {
const zoomLevel = typeof stop[0] === 'object' ? stop[0].zoom : undefined; const zoomLevel = typeof stop[0] === 'object' ? stop[0].zoom : undefined;
const key = this.state.refs[idx]; const key = this.state.refs[idx];
const dataLevel = typeof stop[0] === 'object' ? stop[0].value : stop[0]; const dataLevel = typeof stop[0] === 'object' ? stop[0].value : stop[0];
@ -216,6 +242,7 @@ export default class DataProperty extends React.Component {
</div> </div>
</InputBlock> </InputBlock>
}) })
}
return <div className="maputnik-data-spec-block"> return <div className="maputnik-data-spec-block">
<div className="maputnik-data-spec-property"> <div className="maputnik-data-spec-property">
@ -223,18 +250,6 @@ export default class DataProperty extends React.Component {
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
label={labelFromFieldName(this.props.fieldName)} label={labelFromFieldName(this.props.fieldName)}
> >
<div className="maputnik-data-spec-property-group">
<DocLabel
label="Property"
/>
<div className="maputnik-data-spec-property-input">
<StringInput
value={this.props.value.property}
title={"Input a data property to base styles off of."}
onChange={propVal => this.changeDataProperty("property", propVal)}
/>
</div>
</div>
<div className="maputnik-data-spec-property-group"> <div className="maputnik-data-spec-property-group">
<DocLabel <DocLabel
label="Type" label="Type"
@ -248,6 +263,19 @@ export default class DataProperty extends React.Component {
/> />
</div> </div>
</div> </div>
<div className="maputnik-data-spec-property-group">
<DocLabel
label="Property"
/>
<div className="maputnik-data-spec-property-input">
<StringInput
value={this.props.value.property}
title={"Input a data property to base styles off of."}
onChange={propVal => this.changeDataProperty("property", propVal)}
/>
</div>
</div>
{dataFields &&
<div className="maputnik-data-spec-property-group"> <div className="maputnik-data-spec-property-group">
<DocLabel <DocLabel
label="Default" label="Default"
@ -261,8 +289,11 @@ export default class DataProperty extends React.Component {
/> />
</div> </div>
</div> </div>
}
</InputBlock> </InputBlock>
</div> </div>
{dataFields &&
<>
{dataFields} {dataFields}
<Button <Button
className="maputnik-add-stop" className="maputnik-add-stop"
@ -270,6 +301,8 @@ export default class DataProperty extends React.Component {
> >
Add stop Add stop
</Button> </Button>
</>
}
<Button <Button
className="maputnik-add-stop" className="maputnik-add-stop"
onClick={this.props.onExpressionClick.bind(this)} onClick={this.props.onExpressionClick.bind(this)}

View file

@ -0,0 +1,18 @@
/**
* If we don't have a default value just make one up
*/
export function findDefaultFromSpec (spec) {
if (spec.hasOwnProperty('default')) {
return spec.default;
}
const defaults = {
'color': '#000000',
'string': '',
'boolean': false,
'number': 0,
'array': [],
}
return defaults[spec.type] || '';
}