mirror of
https://github.com/maputnik/editor.git
synced 2026-08-30 00:47:25 +00:00
58edd262b0
This is a rough start on adding react-i18next. I'll be working on adding more translatable strings and translations in the coming days. I'm going to need to wrap class components in HOCs, so let me know if there's something I should be fixing before doing that. I'm thinking now to keep the exported class names exactly the same, and rename the existing classes by prefixing an `I` (for internal). For example: ``` export default class AppToolbar ... ``` becomes ``` class IAppToolbar ... const AppToolbar = withTranslation()(IAppToolbar); export default AppToolbar; ``` I'll be able to contribute Japanese strings (I've talked to a couple people on my team and they'll be happy to help as well), so that's the language I decided to go with in this PR. Closes #746 --------- Co-authored-by: Ko Nagase <nagase@georepublic.co.jp> Co-authored-by: Harel M <harel.mazor@gmail.com>
144 lines
3.7 KiB
TypeScript
144 lines
3.7 KiB
TypeScript
import React from 'react'
|
|
import {MdDelete, MdUndo} from 'react-icons/md'
|
|
import stringifyPretty from 'json-stringify-pretty-compact'
|
|
import { WithTranslation, withTranslation } from 'react-i18next';
|
|
|
|
import Block from './Block'
|
|
import InputButton from './InputButton'
|
|
import labelFromFieldName from '../libs/label-from-field-name'
|
|
import FieldJson from './FieldJson'
|
|
|
|
|
|
type ExpressionPropertyInternalProps = {
|
|
onDelete?(...args: unknown[]): unknown
|
|
fieldName: string
|
|
fieldType?: string
|
|
fieldSpec?: object
|
|
value?: any
|
|
errors?: {[key: string]: {message: string}}
|
|
onChange?(...args: unknown[]): unknown
|
|
onUndo?(...args: unknown[]): unknown
|
|
canUndo?(...args: unknown[]): unknown
|
|
onFocus?(...args: unknown[]): unknown
|
|
onBlur?(...args: unknown[]): unknown
|
|
} & WithTranslation;
|
|
|
|
type ExpressionPropertyState = {
|
|
jsonError: boolean
|
|
};
|
|
|
|
class ExpressionPropertyInternal extends React.Component<ExpressionPropertyInternalProps, ExpressionPropertyState> {
|
|
static defaultProps = {
|
|
errors: {},
|
|
onFocus: () => {},
|
|
onBlur: () => {},
|
|
}
|
|
|
|
constructor(props: ExpressionPropertyInternalProps) {
|
|
super(props);
|
|
this.state = {
|
|
jsonError: false,
|
|
};
|
|
}
|
|
|
|
onJSONInvalid = (_err: Error) => {
|
|
this.setState({
|
|
jsonError: true,
|
|
})
|
|
}
|
|
|
|
onJSONValid = () => {
|
|
this.setState({
|
|
jsonError: false,
|
|
})
|
|
}
|
|
|
|
render() {
|
|
const {t, errors, fieldName, fieldType, value, canUndo} = this.props;
|
|
const {jsonError} = this.state;
|
|
const undoDisabled = canUndo ? !canUndo() : true;
|
|
|
|
const deleteStopBtn = (
|
|
<>
|
|
{this.props.onUndo &&
|
|
<InputButton
|
|
key="undo_action"
|
|
onClick={this.props.onUndo}
|
|
disabled={undoDisabled}
|
|
className="maputnik-delete-stop"
|
|
title={t("Revert from expression")}
|
|
>
|
|
<MdUndo />
|
|
</InputButton>
|
|
}
|
|
<InputButton
|
|
key="delete_action"
|
|
onClick={this.props.onDelete}
|
|
className="maputnik-delete-stop"
|
|
title={t("Delete expression")}
|
|
>
|
|
<MdDelete />
|
|
</InputButton>
|
|
</>
|
|
);
|
|
|
|
const fieldKey = fieldType === undefined ? fieldName : `${fieldType}.${fieldName}`;
|
|
|
|
const fieldError = errors![fieldKey];
|
|
const errorKeyStart = `${fieldKey}[`;
|
|
const foundErrors = [];
|
|
|
|
function getValue(data: any) {
|
|
return stringifyPretty(data, {indent: 2, maxLength: 38})
|
|
}
|
|
|
|
if (jsonError) {
|
|
foundErrors.push({message: "Invalid JSON"});
|
|
}
|
|
else {
|
|
Object.entries(errors!)
|
|
.filter(([key, _error]) => {
|
|
return key.startsWith(errorKeyStart);
|
|
})
|
|
.forEach(([_key, error]) => {
|
|
return foundErrors.push(error);
|
|
})
|
|
|
|
if (fieldError) {
|
|
foundErrors.push(fieldError);
|
|
}
|
|
}
|
|
|
|
return <Block
|
|
// this feels like an incorrect type...? `foundErrors` is an array of objects, not a single object
|
|
error={foundErrors as any}
|
|
fieldSpec={this.props.fieldSpec}
|
|
label={t(labelFromFieldName(this.props.fieldName))}
|
|
action={deleteStopBtn}
|
|
wideMode={true}
|
|
>
|
|
<FieldJson
|
|
mode={{name: "mgl"}}
|
|
lint={{
|
|
context: "expression",
|
|
spec: this.props.fieldSpec,
|
|
}}
|
|
className="maputnik-expression-editor"
|
|
onFocus={this.props.onFocus}
|
|
onBlur={this.props.onBlur}
|
|
onJSONInvalid={this.onJSONInvalid}
|
|
onJSONValid={this.onJSONValid}
|
|
layer={value}
|
|
lineNumbers={false}
|
|
maxHeight={200}
|
|
lineWrapping={true}
|
|
getValue={getValue}
|
|
onChange={this.props.onChange}
|
|
/>
|
|
</Block>
|
|
}
|
|
}
|
|
|
|
const ExpressionProperty = withTranslation()(ExpressionPropertyInternal);
|
|
export default ExpressionProperty;
|