saagie-ui
Version:
Saagie UI from Saagie Design System
180 lines (156 loc) • 4.33 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import { withFormsy, propTypes as formsyPropTypes } from 'formsy-react';
import { Controlled as CodeMirror } from 'react-codemirror2';
import 'codemirror/addon/display/placeholder';
import { FormGroup } from '../../core/molecules/formGroup/FormGroup';
const propTypes = {
...formsyPropTypes,
children: PropTypes.node,
disabled: PropTypes.bool,
/**
* Object of props passed to the <FormGroup> component
*/
groupProps: PropTypes.object,
helper: PropTypes.node,
isHeightAuto: PropTypes.bool,
label: PropTypes.node,
onChange: PropTypes.func,
placeholder: PropTypes.string,
/**
* Object of props passed to the <CodeMirror> element (from `react-codemirror2` package)
*/
codeMirrorProps: PropTypes.object,
/**
* Object of options passed to the <CodeMirror>
* options property (from `react-codemirror2` package)
*/
codeMirrorOptions: PropTypes.object,
};
const defaultProps = {
children: null,
disabled: false,
groupProps: {},
helper: null,
isHeightAuto: false,
label: null,
onChange: () => {},
placeholder: null,
codeMirrorProps: {},
codeMirrorOptions: {},
};
export const FieldCodemirrorUI = ({
// Formsy props
getErrorMessage,
getValue,
isFormSubmitted,
isPristine,
isRequired,
isValid,
setValue,
// Global props
children,
disabled,
groupProps,
helper,
label,
onChange,
placeholder,
// Custom props
codeMirrorProps,
codeMirrorOptions,
isHeightAuto,
}) => {
const [isTouched, setIsTouched] = React.useState(false);
const [isFocused, setIsFocused] = React.useState(false);
const [codeMirrorInstance, setCodeMirrorInstance] = React.useState(null);
const [forceUpdateViewTimeout, setForceUpdateViewTimeout] = React.useState(null);
const isFeedbackVisible = (isTouched && !isPristine()) || isFormSubmitted();
const isError = !isValid() && isFeedbackVisible;
const handleChange = (value) => {
setValue(value);
onChange(value);
};
const handleFocus = () => {
setIsFocused(true);
setIsTouched(true);
};
const handleBlur = () => {
setIsFocused(false);
};
const refreshCodeMirror = () => {
if (!codeMirrorInstance) {
return;
}
codeMirrorInstance.refresh();
};
const clearForceUpdateView = () => {
if (!forceUpdateViewTimeout) {
return;
}
clearTimeout(forceUpdateViewTimeout);
};
const forceUpdateView = () => {
if (!codeMirrorInstance) {
return;
}
clearForceUpdateView();
const previousHeight = codeMirrorInstance.getScrollInfo().height || 0;
setForceUpdateViewTimeout(setTimeout(() => {
const currentHeight = codeMirrorInstance.getScrollInfo().height || 0;
if (
// Don't refresh if no height (CodeMirror is not visible)
currentHeight <= 0
// Don't refresh if same height (before timeout)
|| previousHeight === currentHeight
) {
return;
}
refreshCodeMirror();
}));
};
React.useEffect(() => {
forceUpdateView();
return () => {
clearForceUpdateView();
};
});
return (
<FormGroup
label={label}
helper={helper}
isOptional={!isRequired()}
validationState={!isFocused && isError ? 'danger' : null}
feedbackMessage={isFeedbackVisible && getErrorMessage()}
{...groupProps}
>
<CodeMirror
className={`${isHeightAuto ? 'CodeMirror--heightAuto' : 'CodeMirror--heightSmall'} ${disabled ? 'CodeMirror--readOnly' : ''}`}
value={getValue() || ''}
onBeforeChange={(instance, data, value) => {
handleChange(value);
}}
onFocus={handleFocus}
onBlur={handleBlur}
options={{
mode: 'text',
lineWrapping: true,
lineNumbers: true,
viewportMargin: Infinity,
autoRefresh: true,
readOnly: disabled,
placeholder,
...codeMirrorOptions,
}}
editorDidMount={(editor) => {
setCodeMirrorInstance(editor);
}}
{...codeMirrorProps}
/>
{children}
</FormGroup>
);
};
FieldCodemirrorUI.propTypes = propTypes;
FieldCodemirrorUI.defaultProps = defaultProps;
export const FieldCodemirror = withFormsy(FieldCodemirrorUI);