datajunction-ui
Version:
DataJunction UI
145 lines (134 loc) • 3.87 kB
JSX
/**
* Custom metadata field component for nodes
*/
import { ErrorMessage, Field, useFormikContext } from 'formik';
import CodeMirror from '@uiw/react-codemirror';
import { langs } from '@uiw/codemirror-extensions-langs';
import { useState, useEffect } from 'react';
export const CustomMetadataField = ({ value }) => {
const formik = useFormikContext();
const jsonExt = langs.json();
const [hasError, setHasError] = useState(false);
useEffect(() => {
if (!value || value === '') {
setHasError(false);
return;
}
const stringValue =
typeof value === 'string' ? value : JSON.stringify(value, null, 2);
try {
JSON.parse(stringValue);
setHasError(false);
} catch (err) {
setHasError(true);
}
}, [value]);
const formatValue = value => {
if (value === null || value === undefined) {
return '';
}
if (typeof value === 'string') {
return value;
}
return JSON.stringify(value, null, 2);
};
const updateFormik = val => {
formik.setFieldValue('custom_metadata', val);
formik.setFieldTouched('custom_metadata', true);
if (!val || val.trim() === '') {
setHasError(false);
} else {
try {
JSON.parse(val);
setHasError(false);
} catch (err) {
setHasError(true);
}
}
};
return (
<div className="QueryInput NodeCreationInput">
<details>
<summary style={{ cursor: 'pointer' }}>
<label
style={{
paddingLeft: '3px',
display: 'inline-block',
pointerEvents: 'none',
}}
>
Custom Metadata (JSON)
</label>
</summary>
<ErrorMessage name="custom_metadata" component="span" />
<Field
type="textarea"
style={{ display: 'none' }}
as="textarea"
name="custom_metadata"
id="CustomMetadata"
validate={value => {
if (!value || value.trim() === '') {
return undefined;
}
try {
const parsed = JSON.parse(value);
if (
typeof parsed === 'object' &&
parsed !== null &&
!Array.isArray(parsed)
) {
const keys = Object.keys(parsed);
const originalKeyMatches = value.match(/"([^"]+)"\s*:/g);
if (
originalKeyMatches &&
originalKeyMatches.length > keys.length
) {
return 'Duplicate keys detected';
}
}
return undefined;
} catch (err) {
return 'Invalid JSON format';
}
}}
/>
<div
role="button"
tabIndex={0}
className={`relative flex ${
hasError ? 'bg-red-900/20' : 'bg-[#282a36]'
}`}
style={{
border: hasError ? '2px solid #ef4444' : 'none',
borderRadius: '4px',
boxShadow: hasError ? '0 0 0 1px rgba(239, 68, 68, 0.3)' : 'none',
}}
>
<CodeMirror
id={'custom_metadata'}
name={'custom_metadata'}
extensions={[jsonExt]}
value={formatValue(value)}
placeholder={'{\n "key": "value"\n}'}
options={{
theme: 'default',
lineNumbers: true,
}}
width="100%"
height="200px"
style={{
margin: '0 0 23px 0',
flex: 1,
fontSize: '110%',
textAlign: 'left',
}}
onChange={(value, viewUpdate) => {
updateFormik(value);
}}
/>
</div>
</details>
</div>
);
};