diff --git a/packages/ra-input-rich-text/src/index.js b/packages/ra-input-rich-text/src/index.js
index a9c2636f339..9d594e3473a 100644
--- a/packages/ra-input-rich-text/src/index.js
+++ b/packages/ra-input-rich-text/src/index.js
@@ -1,153 +1,142 @@
import debounce from 'lodash/debounce';
-import React, { Component } from 'react';
+import React, { useRef, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import Quill from 'quill';
-import { addField, FieldTitle } from 'ra-core';
+import { useInput, FieldTitle } from 'ra-core';
import { InputHelperText } from 'ra-ui-materialui';
import { FormHelperText, FormControl, InputLabel } from '@material-ui/core';
-import { withStyles } from '@material-ui/core/styles';
+import { makeStyles } from '@material-ui/core/styles';
import styles from './styles';
-export class RichTextInput extends Component {
- lastValueChange = null;
-
- static propTypes = {
- addLabel: PropTypes.bool.isRequired,
- classes: PropTypes.object,
- input: PropTypes.object,
- label: PropTypes.string,
- meta: PropTypes.object,
- options: PropTypes.object,
- source: PropTypes.string,
- toolbar: PropTypes.oneOfType([
- PropTypes.array,
- PropTypes.bool,
- PropTypes.shape({
- container: PropTypes.array,
- handlers: PropTypes.object,
- }),
- ]),
- fullWidth: PropTypes.bool,
- configureQuill: PropTypes.func,
- };
-
- static defaultProps = {
- addLabel: true,
- options: {}, // Quill editor options
- record: {},
- toolbar: true,
- fullWidth: true,
- };
-
- componentDidMount() {
- const {
- input: { value },
- toolbar,
- options,
- } = this.props;
-
- this.quill = new Quill(this.divRef, {
+const useStyles = makeStyles(styles);
+
+const RichTextInput = ({
+ options = {}, // Quill editor options
+ record = {},
+ toolbar = true,
+ fullWidth = true,
+ configureQuill,
+ helperText = false,
+ label,
+ source,
+ resource,
+ variant,
+ margin = 'dense',
+ ...rest
+}) => {
+ const classes = useStyles();
+ const quillInstance = useRef();
+ const divRef = useRef();
+ const editor = useRef();
+
+ const {
+ id,
+ isRequired,
+ input: { value, onChange },
+ meta: { touched, error },
+ } = useInput({ source, ...rest });
+
+ const lastValueChange = useRef(value);
+
+ const onTextChange = useCallback(
+ debounce(() => {
+ const value =
+ editor.current.innerHTML === '
'
+ ? ''
+ : editor.current.innerHTML;
+ lastValueChange.current = value;
+ onChange(value);
+ }, 500),
+ []
+ );
+
+ useEffect(() => {
+ quillInstance.current = new Quill(divRef.current, {
modules: { toolbar, clipboard: { matchVisual: false } },
theme: 'snow',
...options,
});
- if (this.props.configureQuill) {
- this.props.configureQuill(this.quill);
+ if (configureQuill) {
+ configureQuill(quillInstance.current);
}
- this.quill.setContents(this.quill.clipboard.convert(value));
-
- this.editor = this.divRef.querySelector('.ql-editor');
- this.quill.on('text-change', this.onTextChange);
- }
+ quillInstance.current.setContents(
+ quillInstance.current.clipboard.convert(value)
+ );
- componentDidUpdate() {
- if (this.lastValueChange !== this.props.input.value) {
- const selection = this.quill.getSelection();
- this.quill.setContents(
- this.quill.clipboard.convert(this.props.input.value)
+ editor.current = divRef.current.querySelector('.ql-editor');
+ quillInstance.current.on('text-change', onTextChange);
+
+ return () => {
+ quillInstance.current.off('text-change', onTextChange);
+ onTextChange.cancel();
+ quillInstance.current = null;
+ };
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ useEffect(() => {
+ if (lastValueChange.current !== value) {
+ const selection = quillInstance.current.getSelection();
+ quillInstance.current.setContents(
+ quillInstance.current.clipboard.convert(value)
);
- if (selection && this.quill.hasFocus()) {
- this.quill.setSelection(selection);
+ if (selection && quillInstance.current.hasFocus()) {
+ quillInstance.current.setSelection(selection);
}
}
- }
-
- componentWillUnmount() {
- this.quill.off('text-change', this.onTextChange);
- this.onTextChange.cancel();
- this.quill = null;
- }
-
- onTextChange = debounce(() => {
- const value =
- this.editor.innerHTML === '
'
- ? ''
- : this.editor.innerHTML;
- this.lastValueChange = value;
- this.props.input.onChange(value);
- }, 500);
-
- updateDivRef = ref => {
- this.divRef = ref;
- };
-
- render() {
- const {
- label,
- source,
- resource,
- isRequired,
- id,
- classes = {},
- margin = 'dense',
- variant,
- } = this.props;
- const { touched, error, helperText = false } = this.props.meta;
- return (
-
- {label !== '' && label !== false && (
-
-
-
- )}
-
- {helperText || (touched && error) ? (
-
-
-
- ) : null}
-
- );
- }
-}
-
-const RichTextInputWithField = addField(withStyles(styles)(RichTextInput));
+ }, [value]);
+
+ return (
+
+ {label !== '' && label !== false && (
+
+
+
+ )}
+
+ {helperText || (touched && error) ? (
+
+
+
+ ) : null}
+
+ );
+};
-RichTextInputWithField.defaultProps = {
- fullWidth: true,
+RichTextInput.propTypes = {
+ label: PropTypes.string,
+ options: PropTypes.object,
+ source: PropTypes.string,
+ toolbar: PropTypes.oneOfType([
+ PropTypes.array,
+ PropTypes.bool,
+ PropTypes.shape({
+ container: PropTypes.array,
+ handlers: PropTypes.object,
+ }),
+ ]),
+ fullWidth: PropTypes.bool,
+ configureQuill: PropTypes.func,
};
-export default RichTextInputWithField;
+
+export default RichTextInput;
diff --git a/packages/ra-input-rich-text/src/index.spec.js b/packages/ra-input-rich-text/src/index.spec.js
index efe1a64665a..46a30d7d6e6 100644
--- a/packages/ra-input-rich-text/src/index.spec.js
+++ b/packages/ra-input-rich-text/src/index.spec.js
@@ -1,8 +1,9 @@
import React from 'react';
import debounce from 'lodash/debounce';
import { render, fireEvent, waitForElement } from '@testing-library/react';
+import { Form } from 'react-final-form';
-import { RichTextInput } from './index';
+import RichTextInput from './index';
let container;
@@ -30,12 +31,12 @@ describe('RichTextInput', () => {
const handleChange = jest.fn();
debounce.mockImplementation(fn => fn);
const { getByTestId, rerender } = render(
- test',
- onChange: handleChange,
- }}
- meta={{ error: null }}
+