Better handling of undo history for expressions.

This commit is contained in:
orangemug 2020-02-17 11:29:05 +00:00
parent 029eff9317
commit cff32696cc
3 changed files with 67 additions and 32 deletions

View file

@ -45,27 +45,30 @@ function isPrimative (value) {
} }
function isArrayOfPrimatives (values) { function isArrayOfPrimatives (values) {
if (Array.isArray(value)) { if (Array.isArray(values)) {
return values.every(isPrimative); return values.every(isPrimative);
} }
return false; return false;
} }
function checkIsExpression (value, fieldSpec={}) { function getDataType (value, fieldSpec={}) {
if (value === undefined) { if (value === undefined) {
return false; return "value";
} }
else if (isPrimative(value)) { else if (isPrimative(value)) {
return false; return "value";
} }
else if (fieldSpec.type === "array" && isArrayOfPrimatives(value)) { else if (fieldSpec.type === "array" && isArrayOfPrimatives(value)) {
return false; return "value";
} }
else if (isZoomField(value) || isDataField(value)) { else if (isZoomField(value)) {
return false; return "zoom_function";
}
else if (isDataField(value)) {
return "data_function";
} }
else { else {
return true; return "expression";
} }
} }
@ -111,7 +114,21 @@ export default class FunctionSpecProperty extends React.Component {
constructor (props) { constructor (props) {
super(); super();
this.state = { this.state = {
isExpression: checkIsExpression(props.value, props.fieldSpec), dataType: getDataType(props.value, props.fieldSpec),
isEditing: false,
}
}
static getDerivedStateFromProps(props, state) {
// Because otherwise when editing values we end up accidentally changing field type.
if (state.isEditing) {
return {};
}
else {
return {
isEditing: false,
dataType: getDataType(props.value, props.fieldSpec)
};
} }
} }
@ -150,7 +167,7 @@ export default class FunctionSpecProperty extends React.Component {
const {fieldSpec, fieldName} = this.props; const {fieldSpec, fieldName} = this.props;
this.props.onChange(fieldName, fieldSpec.default); this.props.onChange(fieldName, fieldSpec.default);
this.setState({ this.setState({
isExpression: false, dataType: "value",
}); });
} }
@ -184,25 +201,25 @@ export default class FunctionSpecProperty extends React.Component {
const {value, fieldName} = this.props; const {value, fieldName} = this.props;
if (isLiteralExpression(value)) { if (isLiteralExpression(value)) {
this.props.onChange(fieldName, value[1]); this.props.onChange(fieldName, value[1]);
this.setState({ this.setState({
isExpression: false dataType: "value",
}); });
} }
} }
canUndo = () => { canUndo = () => {
const {value} = this.props; const {value, fieldSpec} = this.props;
return isLiteralExpression(value); return (
isLiteralExpression(value) ||
isPrimative(value) ||
(Array.isArray(value) && fieldSpec.type === "array")
);
} }
makeExpression = () => { makeExpression = () => {
const expression = ["literal", this.props.value || this.props.fieldSpec.default]; const expression = ["literal", this.props.value || this.props.fieldSpec.default];
this.props.onChange(this.props.fieldName, expression); this.props.onChange(this.props.fieldName, expression);
this.setState({
isExpression: true,
});
} }
makeDataFunction = () => { makeDataFunction = () => {
@ -219,11 +236,20 @@ export default class FunctionSpecProperty extends React.Component {
this.props.onChange(this.props.fieldName, dataFunc) this.props.onChange(this.props.fieldName, dataFunc)
} }
onMarkEditing = () => {
this.setState({isEditing: true});
}
onUnmarkEditing = () => {
this.setState({isEditing: false});
}
render() { render() {
const {dataType} = this.state;
const propClass = this.props.fieldSpec.default === this.props.value ? "maputnik-default-property" : "maputnik-modified-property" const propClass = this.props.fieldSpec.default === this.props.value ? "maputnik-default-property" : "maputnik-modified-property"
let specField; let specField;
if (this.state.isExpression) { if (dataType === "expression") {
specField = ( specField = (
<ExpressionProperty <ExpressionProperty
errors={this.props.errors} errors={this.props.errors}
@ -235,10 +261,12 @@ export default class FunctionSpecProperty extends React.Component {
fieldName={this.props.fieldName} fieldName={this.props.fieldName}
fieldSpec={this.props.fieldSpec} fieldSpec={this.props.fieldSpec}
value={this.props.value} value={this.props.value}
onFocus={this.onMarkEditing}
onBlur={this.onUnmarkEditing}
/> />
); );
} }
else if (isZoomField(this.props.value)) { else if (dataType === "zoom_function") {
specField = ( specField = (
<ZoomProperty <ZoomProperty
errors={this.props.errors} errors={this.props.errors}
@ -252,7 +280,7 @@ export default class FunctionSpecProperty extends React.Component {
/> />
) )
} }
else if (isDataField(this.props.value)) { else if (dataType === "data_function") {
specField = ( specField = (
<DataProperty <DataProperty
errors={this.props.errors} errors={this.props.errors}

View file

@ -26,6 +26,8 @@ export default class ExpressionProperty extends React.Component {
static defaultProps = { static defaultProps = {
errors: {}, errors: {},
onFocus: () => {},
onBlur: () => {},
} }
constructor (props) { constructor (props) {
@ -69,6 +71,8 @@ export default class ExpressionProperty extends React.Component {
> >
<JSONEditor <JSONEditor
className="maputnik-expression-editor" className="maputnik-expression-editor"
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
layer={value} layer={value}
lineNumbers={false} lineNumbers={false}
maxHeight={200} maxHeight={200}

View file

@ -35,25 +35,23 @@ class JSONEditor extends React.Component {
lineWrapping: false, lineWrapping: false,
gutters: ["CodeMirror-lint-markers"], gutters: ["CodeMirror-lint-markers"],
getValue: (data) => { getValue: (data) => {
return stringifyPretty(data, {indent: 2, maxLength: 50} ); return stringifyPretty(data, {indent: 2, maxLength: 50});
} },
onFocus: () => {},
onBlur: () => {},
} }
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
isEditing: false, isEditing: false,
prevValue: this.getValue(), prevValue: this.props.getValue(this.props.layer),
}; };
} }
getValue () {
return this.props.getValue(this.props.layer);
}
componentDidMount () { componentDidMount () {
this._doc = CodeMirror(this._el, { this._doc = CodeMirror(this._el, {
value: this.getValue(), value: this.props.getValue(this.props.layer),
mode: { mode: {
name: "javascript", name: "javascript",
json: true json: true
@ -75,12 +73,14 @@ class JSONEditor extends React.Component {
} }
onFocus = () => { onFocus = () => {
this.props.onFocus();
this.setState({ this.setState({
isEditing: true isEditing: true
}); });
} }
onBlur = () => { onBlur = () => {
this.props.onBlur();
this.setState({ this.setState({
isEditing: false isEditing: false
}); });
@ -96,7 +96,7 @@ class JSONEditor extends React.Component {
if (!this.state.isEditing && prevProps.layer !== this.props.layer) { if (!this.state.isEditing && prevProps.layer !== this.props.layer) {
this._cancelNextChange = true; this._cancelNextChange = true;
this._doc.setValue( this._doc.setValue(
this.getValue(), this.props.getValue(this.props.layer),
) )
} }
} }
@ -104,6 +104,9 @@ class JSONEditor extends React.Component {
onChange = (e) => { onChange = (e) => {
if (this._cancelNextChange) { if (this._cancelNextChange) {
this._cancelNextChange = false; this._cancelNextChange = false;
this.setState({
prevValue: this._doc.getValue(),
})
return; return;
} }
const newCode = this._doc.getValue(); const newCode = this._doc.getValue();