Refactored editor component structure

This commit is contained in:
Alex Hunt 2019-07-28 12:11:46 -07:00
parent 006a1b2e1f
commit 2dd56ed8c5
9 changed files with 385 additions and 128 deletions

View File

@ -0,0 +1,27 @@
import React from 'react'
import { ReaderAnnotateButton, ReaderEditButton, EditorToolButton, EditorDeleteToolButton, AnnotatorNewButton, AnnotatorRemoveButton, EditorCancelButton, EditorSubmitButton} from './button'
export const EditorTitleContentBlock = (props) =>
<div id='editor_title_block' className='col-md-12'>
{props.children}
</div>
export const EditorTopBarContentBlock = (props) =>
<div id='editor_top_bar_block' className='col-md-12'>
{props.children}
</div>
export const EditorTextContentBlock = (props) =>
<div id='editor_text_block' className='col-md-12'>
{props.children}
</div>
export const EditorBottomBarContentBlock = (props) =>
<div id='editor_top_bar_block' className='col-md-12'>
{props.children}
</div>
export const EditorAttributeContentBlock = (props) =>
<div id='editor_attribute_block' className='col-md-12'>
{props.children}
</div>

View File

@ -0,0 +1,49 @@
import React from 'react'
import { ReaderAnnotateButton, ReaderEditButton, EditorToolButton, EditorDeleteToolButton, AnnotatorNewButton, AnnotatorRemoveButton, EditorCancelButton, EditorSubmitButton} from './button'
export const EditorReadModeOptions = ({setMode, docType}) =>
<div className='row'>
{['chapters', 'notes'].indexOf(docType) >= 0 &&
<div className='topbar_button col-5'>
<ReaderAnnotateButton onClick={()=>setMode('ANNOTATE_MODE')}/>
</div>
}
<div className='topbar_button col-5 ml-auto'>
<ReaderEditButton onClick={()=>setMode('EDIT_MODE')} />
</div>
</div>
export const EditorEditModeRichTextOptions = ({editorState, onToolButtonClick, disabled}) =>
<div className='row'>
<div className='col-5'>
<div className='btn-group' role='group'>
<EditorToolButton glyph='bold' onClick={()=>onToolButtonClick(editorState, 'BOLD')}/>
<EditorToolButton glyph='italic' onClick={()=>onToolButtonClick(editorState, 'ITALIC')}/>
<EditorToolButton glyph='underline' onClick={()=>onToolButtonClick(editorState, 'UNDERLINE')}/>
<EditorToolButton glyph='header' onClick={()=>onToolButtonClick(editorState, 'header-two')}/>
</div>
</div>
<div className='col-5 offset-2'>
<EditorDeleteToolButton disabled={disabled}/>
</div>
</div>
export const EditorSubmitOptions = ({cancelEdit, onSubmitClick}) =>
<div className='row'>
<div className='col-5'>
<EditorCancelButton onClick={()=>cancelEdit()}/>
</div>
<div className='col-5 offset-2'>
<EditorSubmitButton onClick={onSubmitClick} />
</div>
</div>
export const EditorAnnotateOptions = ({onNewAnnotationClick, onRemoveAnnotationClick, addDisabled, removeDisabled}) =>
<div className='row'>
<div className='col-5'>
<AnnotatorNewButton onClick={onNewAnnotationClick} disabled={addDisabled}/>
</div>
<div className='col-5 offset-2'>
<AnnotatorRemoveButton onClick={onRemoveAnnotationClick} disabled={removeDisabled} />
</div>
</div>

View File

@ -22,7 +22,7 @@ const EditorReadMode = ({
<ReadModeTopBar docType={docType} setMode={setMode} />
</div>
<div id='editor_content' className={'read_mode ' + docType}>
<Editor editorState={editorState} readOnly={true}/>
</div>
</div>

View File

@ -0,0 +1,18 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Editor } from 'draft-js'
export const TextEditorReadOnly = ({editorState}) =>
<div id='read_only_text_editor' className={'text_editor'}>
<Editor editorState={editorState} readOnly={true}/>
</div>
export const TextEditor = ({editorState, handleKeyCommand, onChange}) =>
<div id='enabled_text_editor' className={'text_editor'}>
<Editor editorState={editorState} handleKeyCommand={handleKeyCommand} onChange={onChange}/>
</div>
export const TextEditorAnnotateOnly = ({editorState, handleKeyCommand, onChange}) =>
<div id='enabled_text_editor' className={'text_editor'}>
<Editor editorState={editorState} handleKeyCommand={handleKeyCommand} onChange={onChange}/>
</div>

View File

@ -0,0 +1,91 @@
import React from 'react'
import { connect } from 'react-redux'
import { TextEditorAnnotateOnly } from '../components/textEditor'
import { EditorTitleContentBlock, EditorTopBarContentBlock, EditorTextContentBlock, EditorBottomBarContentBlock } from '../components/editorContentBlock'
import { EditorEditModeRichTextOptions, EditorSubmitOptions, EditorAnnotateOptions } from '../components/editorOptionBlock'
import actions from '../actions'
import DocumentTitle from '../components/documentTitle'
import TagColorPicker from '../components/tagColorPicker'
import LoadingSpinner from '../components/loadingSpinner'
const EditorAnnotateMode = ({
currentDocument,
editorState,
docType,
toggles,
inputs,
onChangeEditorState,
onNewAnnotationClick,
annotateKeyBindings,
onRemoveAnnotationClick,
cancelEdit,
onSubmitClick,
}) =>
<div id='editor_edit_mode' className='editor_wrapper'>
<EditorTitleContentBlock>
<DocumentTitle
docType={docType}
currentDocument={currentDocument}
/>
</EditorTitleContentBlock>
<EditorTopBarContentBlock>
<EditorAnnotateOptions
onNewAnnotationClick={()=>onNewAnnotationClick(editorState.getSelection())}
onRemoveAnnotationClick={()=>onRemoveAnnotationClick(editorState)}
addDisabled={editorState.getSelection().isCollapsed() ? true : false}
removeDisabled={(editorState.getSelection().isCollapsed() ) ? true : false}
/>
</EditorTopBarContentBlock>
<EditorTextContentBlock>
<TextEditorAnnotateOnly
editorState={editorState}
onChange={onChangeEditorState}
keyBindingFn={annotateKeyBindings}
/>
</EditorTextContentBlock>
<EditorBottomBarContentBlock>
<EditorSubmitOptions
cancelEdit={cancelEdit}
onSubmitClick={()=>onSubmitClick(currentDocument, editorState, inputs, docType)}
/>
</EditorBottomBarContentBlock>
</div>
const mapStateToProps = (state, props) => {
return {
currentDocument: state.currentDocument,
docType: state.docType,
editorState: state.editorState,
toggles: state.toggles,
inputs: state.inputs,
}
}
const mapDispatchToProps = dispatch => {
return {
onChangeEditorState: editorState => {
dispatch(actions.updateEditorState(editorState))
},
annotateKeyBindings: () => {
// Prevents editor input in Annotation Mode
return 'handled'
},
cancelEdit: () => {
dispatch(actions.cancelEdit())
},
onNewAnnotationClick: (selectionState) => {
dispatch(actions.addAnnotation(selectionState))
},
onRemoveAnnotationClick: (editorState) => {
dispatch(actions.removeAnnotation(editorState))
},
onSubmitClick: (currentDocument, editorState, inputs, docType) => {
dispatch(actions.submitDocumentEdit(currentDocument, editorState, inputs, docType))
}
}
}
const EditorAnnotateModeContainer = connect(mapStateToProps, mapDispatchToProps)(EditorAnnotateMode)
export default EditorAnnotateModeContainer

View File

@ -3,151 +3,33 @@ import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import actions from '../actions'
import EditorReadMode from '../components/editorReadMode'
import EditorAnnotateMode from '../components/editorAnnotateMode'
import EditorEditMode from '../components/editorEditMode'
import EditorReadModeContainer from '../containers/editorReadModeContainer'
import EditorAnnotateModeContainer from '../containers/editorAnnotateModeContainer'
import EditorEditModeContainer from '../containers/editorEditModeContainer'
const EditorContent = ({
// State
currentDocument,
editorState,
docType,
mode,
toggles,
inputs,
userErrors,
//Dispatch
handleKeyCommand,
onChangeEditorState,
onToolButtonClick,
setMode,
cancelEdit,
onSubmitClick,
onColorSwatchClick,
onDocumentTitleChange,
onColorPickerInputChange,
onNewAnnotationClick,
annotateKeyBindings,
onRemoveAnnotationClick,
}) =>
const EditorContent = ({mode}) =>
<div id='editor_container'>
{mode === 'READ_MODE' &&
<EditorReadMode
currentDocument={currentDocument}
editorState={editorState}
docType={docType}
toggles={toggles}
setMode={setMode}
/>
<EditorReadModeContainer />
}
{mode === 'ANNOTATE_MODE' &&
<EditorAnnotateMode
currentDocument={currentDocument}
editorState={editorState}
docType={docType}
toggles={toggles}
handleKeyCommand={handleKeyCommand}
onNewAnnotationClick={()=>onNewAnnotationClick(editorState.getSelection())}
onRemoveAnnotationClick={()=>onRemoveAnnotationClick(editorState)}
annotateKeyBindings={annotateKeyBindings}
onChangeEditorState={onChangeEditorState}
onToolButtonClick={onToolButtonClick}
setMode={setMode}
cancelEdit={cancelEdit}
onSubmitClick={()=>onSubmitClick(currentDocument, editorState, inputs, docType)}
/>
<EditorAnnotateModeContainer />
}
{mode === 'EDIT_MODE' &&
<EditorEditMode
currentDocument={currentDocument}
editorState={editorState}
docType={docType}
toggles={toggles}
onChangeEditorState={onChangeEditorState}
cancelEdit={cancelEdit}
onSubmitClick={()=>onSubmitClick(currentDocument, editorState, inputs, docType)}
inputs={inputs}
onColorSwatchClick={onColorSwatchClick}
onDocumentTitleChange={onDocumentTitleChange}
onColorPickerInputChange={onColorPickerInputChange}
userErrors={userErrors}
/>
<EditorEditModeContainer />
}
</div>
const mapStateToProps = (state, props) => {
return {
currentDocument: state.currentDocument,
mode: state.mode,
docType: state.docType,
editorState: state.editorState,
inputs: state.inputs,
toggles: state.toggles,
userErrors: state.userErrors,
}
}
const mapDispatchToProps = dispatch => {
return {
onChangeEditorState: editorState => {
dispatch(actions.updateEditorState(editorState))
},
onDocumentTitleChange: input => {
dispatch(actions.updateDocumentTitleInput(input))
},
onColorPickerInputChange: input => {
dispatch(actions.updateColorPickerInput(input))
},
handleKeyCommand: (command, editorState) => {
dispatch(actions.handleEditorKeyCommand(editorState, command))
},
annotateKeyBindings: () => {
// Prevents editor input in Annotation Mode
return 'handled'
},
setMode: (mode) => {
dispatch(actions.setMode(mode))
},
cancelEdit: () => {
dispatch(actions.cancelEdit())
},
onColorSwatchClick: (color) => {
dispatch(actions.selectColorSwatch(color))
},
onNewAnnotationClick: (selectionState) => {
dispatch(actions.addAnnotation(selectionState))
},
onRemoveAnnotationClick: (editorState) => {
dispatch(actions.removeAnnotation(editorState))
},
onToolButtonClick: (editorState, style) => {
dispatch(actions.applyInlineStyles(editorState, style))
},
onSubmitClick: (currentDocument, editorState, inputs, docType) => {
dispatch(actions.submitDocumentEdit(currentDocument, editorState, inputs, docType))
}
}
}
EditorContent.propTypes = {
currentDocument: PropTypes.object,
editorState: PropTypes.object,
inputs: PropTypes.object,
docType: PropTypes.string,
mode: PropTypes.string,
toggles: PropTypes.object,
handleKeyCommand: PropTypes.func,
onChangeEditorState: PropTypes.func,
onToolButtonClick: PropTypes.func,
setMode: PropTypes.func,
cancelEdit: PropTypes.func,
onSubmitClick: PropTypes.func,
onDocumentTitleChange: PropTypes.func,
onNewAnnotationClick: PropTypes.func,
annotateKeyBindings: PropTypes.func,
onRemoveAnnotationClick: PropTypes.func,
mode: PropTypes.string
}
const EditorContentContainer = connect(mapStateToProps, mapDispatchToProps)(EditorContent)
const EditorContentContainer = connect(mapStateToProps)(EditorContent)
export default EditorContentContainer

View File

@ -0,0 +1,113 @@
import React from 'react'
import { connect } from 'react-redux'
import { TextEditor } from '../components/textEditor'
import { EditorTitleContentBlock, EditorTopBarContentBlock, EditorTextContentBlock, EditorBottomBarContentBlock, EditorAttributeContentBlock } from '../components/editorContentBlock'
import { EditorEditModeRichTextOptions, EditorSubmitOptions } from '../components/editorOptionBlock'
import actions from '../actions'
import DocumentTitle from '../components/documentTitle'
import TagColorPicker from '../components/tagColorPicker'
import LoadingSpinner from '../components/loadingSpinner'
const EditorEditMode = ({
currentDocument,
docType,
editorState,
inputs,
userErrors,
handleKeyCommand,
onChangeEditorState,
onDocumentTitleChange,
onColorPickerInputChange,
onColorSwatchClick,
cancelEdit,
onSubmitClick,
onToolButtonClick,
}) =>
<div id='editor_edit_mode' className='editor_wrapper'>
<EditorTitleContentBlock>
<input
type='text'
value={inputs.documentTitle}
placeholder='Document Title'
onChange={onDocumentTitleChange}
/>
</EditorTitleContentBlock>
<EditorTopBarContentBlock>
<EditorEditModeRichTextOptions
editorState={editorState}
onToolButtonClick={onToolButtonClick}
disabled={!currentDocument.id ? true : false}
/>
</EditorTopBarContentBlock>
<EditorTextContentBlock>
<TextEditor
editorState={editorState}
handleKeyCommand={handleKeyCommand}
onChange={onChangeEditorState}
/>
</EditorTextContentBlock>
<EditorAttributeContentBlock>
{docType === 'tags' &&
<TagColorPicker
input={inputs.colorPicker}
onChange={onColorPickerInputChange}
onColorSwatchClick={onColorSwatchClick}
/>
}
</EditorAttributeContentBlock>
<EditorBottomBarContentBlock>
<EditorSubmitOptions
cancelEdit={cancelEdit}
onSubmitClick={()=>onSubmitClick(currentDocument, editorState, inputs, docType)}
/>
</EditorBottomBarContentBlock>
<div id='user_errors'>
{userErrors.map(error =>
<div key={error} className='user_error_message'>{error}</div>
)}
</div>
</div>
const mapStateToProps = (state, props) => {
return {
currentDocument: state.currentDocument,
docType: state.docType,
editorState: state.editorState,
inputs: state.inputs,
userErrors: state.userErrors,
}
}
const mapDispatchToProps = dispatch => {
return {
onChangeEditorState: editorState => {
dispatch(actions.updateEditorState(editorState))
},
onDocumentTitleChange: input => {
dispatch(actions.updateDocumentTitleInput(input))
},
onColorPickerInputChange: input => {
dispatch(actions.updateColorPickerInput(input))
},
handleKeyCommand: (command, editorState) => {
dispatch(actions.handleEditorKeyCommand(editorState, command))
},
cancelEdit: () => {
dispatch(actions.cancelEdit())
},
onColorSwatchClick: (color) => {
dispatch(actions.selectColorSwatch(color))
},
onToolButtonClick: (editorState, style) => {
dispatch(actions.applyInlineStyles(editorState, style))
},
onSubmitClick: (currentDocument, editorState, inputs, docType) => {
dispatch(actions.submitDocumentEdit(currentDocument, editorState, inputs, docType))
}
}
}
const EditorEditModeContainer = connect(mapStateToProps, mapDispatchToProps)(EditorEditMode)
export default EditorEditModeContainer

View File

@ -0,0 +1,59 @@
import React from 'react'
import PropTypes from 'prop-types'
import { Editor } from 'draft-js'
import { connect } from 'react-redux'
import { TextEditorReadOnly } from '../components/textEditor'
import { EditorTitleContentBlock, EditorTopBarContentBlock, EditorTextContentBlock } from '../components/editorContentBlock'
import { EditorReadModeOptions } from '../components/editorOptionBlock'
import actions from '../actions'
import DocumentTitle from '../components/documentTitle'
const EditorReadMode = ({
currentDocument,
editorState,
docType,
toggles,
setMode
}) =>
<div id='editor_read_mode' className='editor_wrapper'>
<EditorTitleContentBlock>
<DocumentTitle
docType={docType}
currentDocument={currentDocument}
/>
</EditorTitleContentBlock>
<EditorTopBarContentBlock>
<EditorReadModeOptions
setMode={setMode}
docType={docType}
/>
</EditorTopBarContentBlock>
<EditorTextContentBlock>
<TextEditorReadOnly
editorState={editorState}
/>
</EditorTextContentBlock>
</div>
const mapStateToProps = (state, props) => {
return {
currentDocument: state.currentDocument,
editorState: state.editorState,
docType: state.docType,
toggles: state.toggles,
mode: state.mode,
}
}
const mapDispatchToProps = dispatch => {
return {
setMode: (mode) => {
dispatch(actions.setMode(mode))
},
}
}
const EditorReadModeContainer = connect(mapStateToProps, mapDispatchToProps)(EditorReadMode)
export default EditorReadModeContainer

View File

@ -1,6 +1,24 @@
@import "variables";
@import "mixin";
.text_editor {
max-height: 72vh;
overflow-y: auto;
background-color: rgba(256,256,256,.8);
box-shadow: 2px 5px 10px 1px rgba(0, 0, 0, 0.3);
margin: 1vh 0;
border: 1px solid $border_color;
border-radius: 5px;
padding: 2% 5%;
font-size: 0.8em;
}
// PRE-FACTOR
#editor_container {
height: 82vh;
}