react-conventions
Version:
An open source set of React components that implement Ambassador's Design and UX patterns.
162 lines (139 loc) • 3.93 kB
JavaScript
import React from 'react'
import classNames from 'classnames/bind'
import Quill from 'quill'
import shallowCompare from 'react-addons-shallow-compare'
import Toolbar from './Toolbar'
import style from './style.scss'
import '../../styles/global/quill.scss'
/**
* The TextEditor component.
*/
class TextEditor extends React.Component {
constructor(props) {
super(props)
this.getHTML = () => {
if (this._editor) {
return this._editor.firstChild.innerHTML
}
}
}
state = {
value: this.props.value
}
static defaultProps = {
disabled: false,
value: ''
}
static propTypes = {
/**
* Whether the text editor is disabled.
*/
disabled: React.PropTypes.bool,
/**
* Value of the text editor (HTML).
*/
value: React.PropTypes.string,
/**
* Optional placeholder text.
*/
placeholder: React.PropTypes.string,
/**
* Name of the text editor.
*/
name: React.PropTypes.string,
/**
* Optional styles to add to the text editor.
*/
optClass: React.PropTypes.string,
/**
* A callback function to be called when the text editor changes.
*/
changeCallback: React.PropTypes.func,
/**
* Merge tags to display in the toolbar.
*/
mergeTags: React.PropTypes.array
}
registerEventHandlers = () => {
// On text change
this.state.textEditor.on('text-change', (delta, oldDelta, source) => {
const value = this.getHTML()
if (value !== this.state.value) {
this.handleChange(value)
}
})
}
handleChange = (value) => {
const event = {
target: {
name: this.props.name,
value: value
}
}
// Set state before triggering the callback
this.setState({ value: value }, () => {
if (this.props.changeCallback) {
this.props.changeCallback(event)
}
})
}
setContent = (value) => {
this._editor.firstChild.innerHTML = value
}
componentDidMount = () => {
// Define editor options
const options = {
modules: {
toolbar: this._toolbar
},
placeholder: this.props.placeholder || '',
theme: 'snow'
}
// Use style attribute for align and font tools
const AlignAttribute = Quill.import('attributors/style/align')
const FontAttribute = Quill.import('attributors/style/font')
Quill.register(AlignAttribute, true)
Quill.register(FontAttribute, true)
// Initialize the editor
this.setState({ textEditor: new Quill(this._editor, options) }, () => {
// Set the content
if (this.props.value) {
this.setContent(this.props.value)
}
// Disable the editor
if (this.props.disabled) {
this.state.textEditor.disable()
}
// Register event handlers
this.registerEventHandlers()
})
}
componentWillReceiveProps = (nextProps) => {
// Enable/disable the editor
this.state.textEditor.enable(!nextProps.disabled)
// If the value changed set the editor content and state
if (nextProps.value !== this.state.value) {
this.setContent(nextProps.value)
this.setState({ value: nextProps.value })
}
}
shouldComponentUpdate = (nextProps, nextState) => {
return shallowCompare(this, nextProps, nextState)
}
setToolbarRef = (toolbar) => {
this._toolbar = toolbar
}
render = () => {
const cx = classNames.bind(style)
const disabledClass = this.props.disabled ? style['editor-disabled'] : ''
const editorClass = cx(style['editor-component'], this.props.optClass, disabledClass)
return (
<div className={editorClass}>
<Toolbar textEditor={this.state.textEditor} mergeTags={this.props.mergeTags} onMount={this.setToolbarRef} />
<div ref={(c) => this._editor = c}></div>
<div className={style.overlay}></div>
</div>
)
}
}
export default TextEditor