UNPKG

saagie-ui

Version:

Saagie UI from Saagie Design System

180 lines (156 loc) 4.33 kB
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);