@limetech/lime-elements
Version:
102 lines (101 loc) • 3.47 kB
JavaScript
import React from "react";
import { isEmpty } from "lodash-es";
export class CodeEditor extends React.Component {
constructor(props) {
super(props);
this.state = { validationError: '', modified: false };
this.handleChange = this.handleChange.bind(this);
}
render() {
const props = this.props;
let value = '{}';
try {
value = JSON.stringify(props.formData, null, ' ');
}
catch (_a) {
// N/A
}
const { validationError, modified } = this.state;
const { errorSchema, formData, required } = props;
const hasSchemaErrors = !isEmpty(errorSchema);
const hasValue = formData !== undefined && formData !== null;
const shouldShowSchemaErrors = hasSchemaErrors && (modified || hasValue || !required);
const isInvalid = validationError.length > 0 || shouldShowSchemaErrors;
let helperText;
if (validationError) {
helperText = validationError;
}
else if (shouldShowSchemaErrors) {
helperText = findFirstError(errorSchema);
}
return React.createElement('limel-code-editor', {
value: value,
language: 'json',
lineNumbers: true,
fold: true,
lint: true,
onChange: this.handleChange,
invalid: isInvalid,
helperText: helperText,
});
}
componentDidUpdate(prevProps) {
if (prevProps.formData !== this.props.formData &&
(this.state.validationError !== '' || this.state.modified)) {
this.setState({ validationError: '', modified: false });
}
}
handleChange(event) {
const props = this.props;
event.stopPropagation();
if (!props.onChange) {
return;
}
let value;
try {
value = JSON.parse(event.nativeEvent.detail);
}
catch (error) {
const validationError = error instanceof SyntaxError
? `Invalid JSON: ${error.message}`
: 'Should be a valid JSON document';
this.setState({ validationError, modified: true });
return;
}
props.onChange(value);
this.setState({ validationError: '', modified: true });
}
}
/**
* Recursively finds the first error message in a potentially nested errorSchema.
* Checks for __errors at the current level, then recurses into nested objects.
* When an error is found in a nested property, the property name is prepended
* to the message for context (e.g. `"method: is a required property"`).
*
* @param schema - The error schema to search for errors
* @returns The first error message found, or undefined if no errors
*/
function findFirstError(schema) {
if (!schema || typeof schema !== 'object') {
return undefined;
}
if ('__errors' in schema && Array.isArray(schema.__errors)) {
const errors = schema.__errors;
if (errors.length > 0) {
return errors[0];
}
}
for (const key of Object.keys(schema)) {
if (key === '__errors') {
continue;
}
const nested = schema[key];
if (nested && typeof nested === 'object') {
const found = findFirstError(nested);
if (found) {
return `${key}: ${found}`;
}
}
}
return undefined;
}