0

I am building a reactjs application.

I've created a variant of the RichEditor - which uses draft.js -- I am unsure though how to set content for this editor and also how to retrieve it if it were to be made part of a form.

enter image description here

here is my current code base

So if I were to treat it as a field.

const initialValues = {editor: "I am a <h2>bit</h2> of dummy <b>html</b> text"}

<RichEditor initialValues={initialValues} />

^ so something like this - but also how would I obtain the contents if it were part of like a react-redux form?

like this?

import React from 'react'
import { Field, reduxForm } from 'redux-form'
import renderField from '../_SharedFormComponents/renderField'
import validate from './validateComplaint'
import warn from './warnComplaint'

const ComplaintSyncValidationForm= props => {
  const { handleSubmit, pristine, reset, submitting } = props

  const initialValues = {editor: "I am a <h2>bit</h2> of dummy <b>html</b> text"}
  return (
    <form onSubmit={handleSubmit}>
      <Field name="email" type="email" component={renderField} label="Email" />
      <RichEditor initialValues={initialValues} />
      <div>
        <button type="submit" disabled={submitting}>
          Complain
        </button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>
          Clear Values
        </button>
      </div>
    </form>
  )
}

export default reduxForm({
  form: 'syncValidationComplaint', // a unique identifier for this form
  validate, // <--- validation function given to redux-form
  warn // <--- warning function given to redux-form
})(ComplaintSyncValidationForm)

//RichEditor.js

import React from 'react';
import ReactDOM from 'react-dom';
import {Editor, EditorState, RichUtils} from 'draft-js';

import './RichEditor.scss';


  class RichEditor extends React.Component {
    constructor(props) {
      super(props);
      this.state = {editorState: EditorState.createEmpty()};
      this.focus = () => this.refs.editor.focus();
      this.onChange = (editorState) => this.setState({editorState});
      this.handleKeyCommand = this._handleKeyCommand.bind(this);
      this.onTab = this._onTab.bind(this);
      this.toggleBlockType = this._toggleBlockType.bind(this);
      this.toggleInlineStyle = this._toggleInlineStyle.bind(this);
    }
    _handleKeyCommand(command, editorState) {
      const newState = RichUtils.handleKeyCommand(editorState, command);
      if (newState) {
        this.onChange(newState);
        return true;
      }
      return false;
    }
    _onTab(e) {
      const maxDepth = 4;
      this.onChange(RichUtils.onTab(e, this.state.editorState, maxDepth));
    }
    _toggleBlockType(blockType) {
      this.onChange(
        RichUtils.toggleBlockType(
          this.state.editorState,
          blockType
        )
      );
    }
    _toggleInlineStyle(inlineStyle) {
      this.onChange(
        RichUtils.toggleInlineStyle(
          this.state.editorState,
          inlineStyle
        )
      );
    }
    render() {
      const {editorState} = this.state;
      // If the user changes block type before entering any text, we can
      // either style the placeholder or hide it. Let's just hide it now.
      let className = 'RichEditor-editor';
      var contentState = editorState.getCurrentContent();
      if (!contentState.hasText()) {
        if (contentState.getBlockMap().first().getType() !== 'unstyled') {
          className += ' RichEditor-hidePlaceholder';
        }
      }
      return (
        <div className="RichEditor-root">
          <BlockStyleControls
            editorState={editorState}
            onToggle={this.toggleBlockType}
          />
          <InlineStyleControls
            editorState={editorState}
            onToggle={this.toggleInlineStyle}
          />
          <div className={className} onClick={this.focus}>
            <Editor
              blockStyleFn={getBlockStyle}
              customStyleMap={styleMap}
              editorState={editorState}
              handleKeyCommand={this.handleKeyCommand}
              onChange={this.onChange}
              onTab={this.onTab}
              placeholder="Tell a story..."
              ref="editor"
              spellCheck={true}
            />
          </div>
        </div>
      );
    }
  }



      // Custom overrides for "code" style.
      const styleMap = {
        CODE: {
          backgroundColor: 'rgba(0, 0, 0, 0.05)',
          fontFamily: '"Inconsolata", "Menlo", "Consolas", monospace',
          fontSize: 16,
          padding: 2,
        },
      };
      function getBlockStyle(block) {
        switch (block.getType()) {
          case 'blockquote': return 'RichEditor-blockquote';
          default: return null;
        }
      }
      class StyleButton extends React.Component {
        constructor() {
          super();
          this.onToggle = (e) => {
            e.preventDefault();
            this.props.onToggle(this.props.style);
          };
        }
        render() {
          let className = 'RichEditor-styleButton';
          if (this.props.active) {
            className += ' RichEditor-activeButton';
          }
          return (
            <span className={className} onMouseDown={this.onToggle}>
              {this.props.label}
            </span>
          );
        }
      }
      const BLOCK_TYPES = [
        {label: 'H1', style: 'header-one'},
        {label: 'H2', style: 'header-two'},
        {label: 'H3', style: 'header-three'},/*
        {label: 'H4', style: 'header-four'},
        {label: 'H5', style: 'header-five'},
        {label: 'H6', style: 'header-six'},
        {label: 'Blockquote', style: 'blockquote'},
        {label: 'UL', style: 'unordered-list-item'},
        {label: 'OL', style: 'ordered-list-item'},
        {label: 'Code Block', style: 'code-block'},*/
      ];
      const BlockStyleControls = (props) => {
        const {editorState} = props;
        const selection = editorState.getSelection();
        const blockType = editorState
          .getCurrentContent()
          .getBlockForKey(selection.getStartKey())
          .getType();
        return (
          <div className="RichEditor-controls">
            {BLOCK_TYPES.map((type) =>
              <StyleButton
                key={type.label}
                active={type.style === blockType}
                label={type.label}
                onToggle={props.onToggle}
                style={type.style}
              />
            )}
          </div>
        );
      };
      var INLINE_STYLES = [
        {label: 'Bold', style: 'BOLD'},
        {label: 'Italic', style: 'ITALIC'},
        {label: 'Underline', style: 'UNDERLINE'},
        //{label: 'Monospace', style: 'CODE'},
      ];
      const InlineStyleControls = (props) => {
        var currentStyle = props.editorState.getCurrentInlineStyle();
        return (
          <div className="RichEditor-controls">
            {INLINE_STYLES.map(type =>
              <StyleButton
                key={type.label}
                active={currentStyle.has(type.style)}
                label={type.label}
                onToggle={props.onToggle}
                style={type.style}
              />
            )}
          </div>
        );
      };

export default RichEditor

and the scss for it.

//RichEditor.scss

@import '../../colors.scss';

.RichEditor-root {
  background: $white;
  border: 1px solid $stone-grey;
  font-family: 'Georgia', serif;
  font-size: 14px;
  padding: 15px;
}

.RichEditor-hidePlaceholder .public-DraftEditorPlaceholder-root {
  display: none;
} 

.RichEditor-controls {
  font-family: 'Helvetica', sans-serif;
  font-size: 14px;
  margin-bottom: 5px;
  user-select: none;
}

.RichEditor-styleButton {
  color: $grey;
  cursor: pointer;
  margin-right: 16px;
  padding: 2px 0;
  display: inline-block;
}

.RichEditor-activeButton {
  color: $light-blue;
}

.RichEditor-editor {
  border-top: 1px solid $grey;
  color: black;
  cursor: text;
  font-size: 16px;
  margin-top: 10px;

    .RichEditor-blockquote {
      border-left: 5px solid $stone-grey;
      color: #666;
      font-family: 'Hoefler Text', 'Georgia', serif;
      font-style: italic;
      margin: 16px 0;
      padding: 10px 20px;
    }

    .public-DraftStyleDefault-pre {
      background-color: rgba(0, 0, 0, 0.05);
      font-family: 'Inconsolata', 'Menlo', 'Consolas', monospace;
      font-size: 16px;
      padding: 20px;
    }

    .public-DraftEditorPlaceholder-root,
    .public-DraftEditor-content {
      margin: 0 -15px -15px;
      padding: 15px;
    }

    .public-DraftEditor-content {
      min-height: 100px;
    }

    h2{
        color: $black!important;
    }
}
The Old County
  • 89
  • 13
  • 59
  • 129
  • or maybe even generate a master/slave relationship with a regular textarea box - that is hidden on the interface? – The Old County Oct 17 '17 at 11:58
  • https://stackoverflow.com/questions/35884112/draftjs-how-to-initiate-an-editor-with-content/36007299 – The Old County Oct 17 '17 at 12:54
  • -- I got it to take an initial value from inside the constructor -- but the markup needs to go through a html conversion -- class RichEditor extends React.Component { constructor(props) { super(props); //this.state = {editorState: EditorState.createEmpty()}; var initialMarkup = 'This is a

    test

    for the rich text editor'; this.state = {editorState: EditorState.createWithContent(ContentState.createFromText(initialMarkup))};
    – The Old County Oct 17 '17 at 13:12
  • import {convertFromHTML, Editor, EditorState, RichUtils, ContentState} from 'draft-js'; import './RichEditor.scss'; class RichEditor extends React.Component { constructor(props) { super(props); //this.state = {editorState: EditorState.createEmpty()}; const html = 'This is a

    test

    for the rich text editor'; const blocksFromHTML = convertFromHTML(html); const content = ContentState.createFromBlockArray(blocksFromHTML) this.state = {editorState: EditorState.createWithContent(content)};
    – The Old County Oct 17 '17 at 13:21
  • -- converted the text -- now need to try and pass this from the parent shell – The Old County Oct 17 '17 at 13:21
  • https://github.com/larkintuckerllc/hello-draft/blob/master/src/Contact.jsx – The Old County Oct 18 '17 at 23:27

1 Answers1

0

I made the richtexteditor part of a react-redux component. It updates a hidden field on keyups.. it has to change the value instead of setting the state - this was to fool the react-redux form that the field had been "touched" --- although its not as fully sophisitcated as I would like - as the clear values button if shown - will reset the hidden field but not restore the old text in the editor.

//renderRichFieldTextarea react-redux field.

import React from 'react'
import RichEditor from '../_SharedGlobalComponents/RichEditor';

class ContentContainer extends React.Component {
      constructor(props) {
        super(props);

        this.state = {
          value: this.props.input.value
        };

        this.handler = this.handler.bind(this);
      }

      handler(d) {
        this.props.input.onChange(d);
      }

      render() {
        const input = this.props.input;
        const placeholder = this.props.placeholder;
        const type = this.props.type;
        const boxclass = this.props.boxclass;

        return (
            <div>
              <RichEditor 
              initialValues={input.value} 
              handler={this.handler} 
              />
            <input {...input} type="hidden" />
            </div>
        );
      }
    }

const renderRichFieldTextarea = ({input, label, placeholder, boxclass, type, meta: {touched, error, warning}}) => (
  <div className='field'>
    <div>
      <label>{label}</label>
    </div>
    <div>
          <ContentContainer initialValues={input.value} onChange={this.onChange} input={input} placeholder={placeholder} type={type} boxclass={boxclass}/>
    </div>
  </div>
)

export default renderRichFieldTextarea

so I use it in a form component like this.

//EditPrivacySectionsSyncValidationForm - a react redux form

import React from 'react'
import { Field, reduxForm } from 'redux-form'
import { Row, Col, Collapse } from 'antd';

import renderField from '../_SharedFormComponents/renderField'
import renderRichFieldTextarea from '../_SharedFormComponents/renderRichFieldTextarea'
import renderRadioCheckField from '../_SharedFormComponents/renderRadioCheckField'

import renderError from '../_SharedFormComponents/renderError'

import validate from './validateEditSections'
import warn from './warnEditSections'

const Panel = Collapse.Panel;

const EditPrivacySectionsSyncValidationForm = props => {
  const { handleSubmit, pristine, reset, submitting } = props

  return (
    <form onSubmit={handleSubmit} >

      <Row>
        <Col xs={24} sm={24} md={24}>
          <div>
            <Field name="privacy" type="textarea" component={renderRichFieldTextarea} label="Privacy" placeholder="Please write here..." boxclass="tall"/>
          </div>          
        </Col>
      </Row>

      <div>
        <button type="submit" disabled={submitting}>
          Submit
        </button>{/*
        <button type="button" disabled={pristine || submitting} onClick={reset}>
          Clear Values
        </button>*/}
      </div>

    </form>
  )
}

//initialValues = {interest: "art"}
export default reduxForm({
  form: 'syncValidationEditPrivacySections', // a unique identifier for this form
  validate, // <--- validation function given to redux-form
  warn // <--- warning function given to redux-form
})(EditPrivacySectionsSyncValidationForm)
The Old County
  • 89
  • 13
  • 59
  • 129