mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-28 18:31:17 +01:00
Better handling of undo history for expressions.
This commit is contained in:
parent
029eff9317
commit
cff32696cc
3 changed files with 67 additions and 32 deletions
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue