react-quill
Advanced tools
Comparing version 0.1.1 to 0.2.0
Changelog | ||
========= | ||
v0.2.0 | ||
------ | ||
- Fix React warnings about unique `key` props in toolbar (@Janekk). | ||
- Sending `delta` and `source` from editor change events. Fixes #17. | ||
- Rewritten uncontrolled and semi-controlled operation. Should fix #9, #10 and #14. | ||
- Editor props can now be changed after mounting. | ||
- Added callback for selection change event. Closes #12. | ||
v0.1.1 | ||
------ | ||
- The pre-compiled distributable is not shipped with the NPM package anymore. Should fix [#2](https://github.com/zenoamaro/react-quill/issues/2). | ||
- The pre-compiled distributable is not shipped with the NPM package anymore. Should fix #2. | ||
- Sourcemaps are now emitted for both distributables, as separate files. | ||
@@ -8,0 +16,0 @@ - Avoiding parsing Quill as it ships with a pre-built main. |
{ | ||
"name": "react-quill", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"description": "The Quill rich-text editor as a React component.", | ||
@@ -5,0 +5,0 @@ "author": "zenoamaro <zenoamaro@gmail.com>", |
@@ -12,4 +12,2 @@ React-Quill ![](https://travis-ci.org/zenoamaro/react-quill.svg?branch=master) | ||
**Warning**: The project is still in alpha stage. Use with caution. | ||
1. [Quick start](#quick-start) | ||
@@ -27,2 +25,8 @@ 2. [API reference](#api-reference) | ||
~~~jsx | ||
/* | ||
Include `quill.base.css` to give the editor some basic styles it needs. | ||
You can find the _base_ theme in the quill distribution or inside | ||
`node_modules`. | ||
*/ | ||
var React = require('react'); | ||
@@ -45,2 +49,8 @@ var ReactQuill = require('react-quill'); | ||
~~~jsx | ||
/* | ||
Include a theme like `quill.snow.css` and activate it in the | ||
configuration like shown below. You can find the _snow_ theme in the | ||
quill distribution or inside `node_modules`. | ||
*/ | ||
var MyComponent = React.createClass({ | ||
@@ -123,3 +133,3 @@ /* ... */ | ||
`value` | ||
: Value for the editor as a controlled component. | ||
: Value for the editor as a controlled component. Note that due to limitations in Quill, this is actually a _semi-controlled_ mode, meaning that the edit is not prevented, but changing `value` will still replace the contents. | ||
@@ -147,6 +157,9 @@ `defaultValue` | ||
`onChange(value)` | ||
`onChange(value, delta, source)` | ||
: Called back with the new contents of the editor after change. | ||
`onChangeSelection(range, source)` | ||
: Called back with the new selected range, or null when unfocused. | ||
Building and testing | ||
@@ -173,2 +186,9 @@ -------------------- | ||
--------- | ||
#### v0.2.0 | ||
- Fix React warnings about unique `key` props in toolbar (@Janekk). | ||
- Sending `delta` and `source` from editor change events. Fixes #17. | ||
- Rewritten uncontrolled and semi-controlled operation. Should fix #9, #10 and #14. | ||
- Editor props can now be changed after mounting. | ||
- Added callback for selection change event. Closes #12. | ||
#### v0.1.1 | ||
@@ -190,6 +210,4 @@ - The pre-compiled distributable is not shipped with the NPM package anymore. Should fix [#2](https://github.com/zenoamaro/react-quill/issues/2). | ||
------- | ||
- [ ] Support updates in editor life-cycle | ||
- [ ] First-class support for modules | ||
- [ ] Better API for custom controls? | ||
- [ ] Delta updates | ||
@@ -196,0 +214,0 @@ |
@@ -21,15 +21,30 @@ 'use strict'; | ||
propTypes: { | ||
id: T.string, | ||
className: T.string, | ||
value: T.string, | ||
id: T.string, | ||
className: T.string, | ||
style: T.object, | ||
value: T.string, | ||
defaultValue: T.string, | ||
readOnly: T.bool, | ||
toolbar: T.array, | ||
formats: T.array, | ||
styles: T.object, | ||
theme: T.string, | ||
readOnly: T.bool, | ||
toolbar: T.array, | ||
formats: T.array, | ||
styles: T.object, | ||
theme: T.string, | ||
pollInterval: T.number, | ||
onChange: T.func | ||
onChange: T.func, | ||
onChangeSelection: T.func | ||
}, | ||
/* | ||
Changing one of these props should cause a re-render. | ||
*/ | ||
dirtyProps: [ | ||
'id', | ||
'className', | ||
'toolbar', | ||
'formats', | ||
'styles', | ||
'theme', | ||
'pollInterval' | ||
], | ||
getDefaultProps: function() { | ||
@@ -44,19 +59,37 @@ return { | ||
/* | ||
Retrieve the initial value from either `value` (preferred) | ||
or `defaultValue` if you want an un-controlled component. | ||
We consider the component to be controlled if | ||
whenever `value` is bein sent in props. | ||
*/ | ||
isControlled: function() { | ||
return 'value' in this.props; | ||
}, | ||
getInitialState: function() { | ||
return {}; | ||
return { | ||
value: this.isControlled() | ||
? this.props.value | ||
: this.props.defaultValue | ||
}; | ||
}, | ||
/* | ||
Update only if we've been passed a new `value`. | ||
This leaves components using `defaultValue` alone. | ||
*/ | ||
componentWillReceiveProps: function(nextProps) { | ||
var editor = this.state.editor; | ||
// Update only if we've been passed a new `value`. | ||
// This leaves components using `defaultValue` alone. | ||
if ('value' in nextProps) { | ||
if (nextProps.value !== this.props.value) { | ||
this.setEditorContents(this.state.editor, nextProps.value); | ||
// NOTE: Seeing that Quill is missing a way to prevent | ||
// edits, we have to settle for a hybrid between | ||
// controlled and uncontrolled mode. We can't prevent | ||
// the change, but we'll still override content | ||
// whenever `value` differs from current state. | ||
if (nextProps.value !== this.getEditorContents()) { | ||
this.setEditorContents(editor, nextProps.value); | ||
} | ||
} | ||
// We can update readOnly state in-place. | ||
if ('readOnly' in nextProps) { | ||
if (nextProps.readOnly !== this.props.readOnly) { | ||
this.setEditorReadOnly(editor, nextProps.readOnly); | ||
} | ||
} | ||
}, | ||
@@ -78,3 +111,10 @@ | ||
shouldComponentUpdate: function(nextProps, nextState) { | ||
// Never re-render or we lose the element. | ||
// Check if one of the changes should trigger a re-render. | ||
for (var i=0; i<this.dirtyProps.length; i++) { | ||
var prop = this.dirtyProps[i]; | ||
if (nextProps[prop] !== this.props[prop]) { | ||
return true; | ||
} | ||
} | ||
// Never re-render otherwise. | ||
return false; | ||
@@ -123,7 +163,7 @@ }, | ||
getEditorContents: function() { | ||
return this.props.value || this.props.defaultValue || ''; | ||
return this.state.value; | ||
}, | ||
getClassName: function() { | ||
return ['quill', this.props.className].join(' '); | ||
getEditorSelection: function() { | ||
return this.state.selection; | ||
}, | ||
@@ -140,10 +180,12 @@ | ||
return [ | ||
// Quill modifies these elements in-place, | ||
// so we need to re-render them every time. | ||
QuillToolbar({ | ||
key:'toolbar', | ||
ref:'toolbar', | ||
key: 'toolbar-' + Math.random(), | ||
ref: 'toolbar', | ||
items: this.props.toolbar | ||
}), | ||
React.DOM.div({ | ||
key:'editor', | ||
ref:'editor', | ||
key: 'editor-' + Math.random(), | ||
ref: 'editor', | ||
className: 'quill-contents', | ||
@@ -158,3 +200,5 @@ dangerouslySetInnerHTML: { __html:this.getEditorContents() } | ||
return React.DOM.div({ | ||
className: this.getClassName(), | ||
id: this.props.id, | ||
style: this.props.style, | ||
className: 'quill ' + this.props.className, | ||
onChange: this.preventDefault }, | ||
@@ -165,10 +209,7 @@ this.renderContents() | ||
/* | ||
Updates the local state with the new contents, | ||
executes the change handler passed as props. | ||
*/ | ||
onEditorChange: function(value) { | ||
if (value !== this.state.value) { | ||
onEditorChange: function(value, delta, source) { | ||
if (value !== this.getEditorContents()) { | ||
this.setState({ value: value }); | ||
if (this.props.onChange) { | ||
this.props.onChange(value); | ||
this.props.onChange(value, delta, source); | ||
} | ||
@@ -178,2 +219,13 @@ } | ||
onEditorChangeSelection: function(range, source) { | ||
var s = this.getEditorSelection() || {}; | ||
var r = range || {}; | ||
if (r.start !== s.start || r.end !== s.end) { | ||
this.setState({ selection: range }); | ||
if (this.props.onChangeSelection) { | ||
this.props.onChangeSelection(range, source); | ||
} | ||
} | ||
}, | ||
/* | ||
@@ -180,0 +232,0 @@ Stop change events from the toolbar from |
/* | ||
React-Quill v0.1.0 | ||
React-Quill v0.2.0 | ||
https://github.com/zenoamaro/react-quill | ||
@@ -4,0 +4,0 @@ */ |
@@ -18,17 +18,13 @@ 'use strict'; | ||
hookEditor: function(editor) { | ||
var self = this; | ||
editor.on('text-change', function(delta, source) { | ||
if (self.onEditorChange) { | ||
self.onEditorChange(editor.getHTML(), delta, source); | ||
if (this.onEditorChange) { | ||
this.onEditorChange(editor.getHTML(), delta, source); | ||
} | ||
}); | ||
}, | ||
}.bind(this)); | ||
updateEditor: function(editor, config) { | ||
// NOTE: This tears the editor down, and reinitializes | ||
// it with the new config. Ugly but necessary | ||
// as there is no api for updating it. | ||
this.destroyEditor(editor); | ||
this.createEditor(config); | ||
return editor; | ||
editor.on('selection-change', function(range, source) { | ||
if (this.onEditorChangeSelection) { | ||
this.onEditorChangeSelection(range, source); | ||
} | ||
}.bind(this)); | ||
}, | ||
@@ -40,2 +36,7 @@ | ||
setEditorReadOnly: function(editor, value) { | ||
value? editor.editor.disable() | ||
: editor.editor.enable(); | ||
}, | ||
/* | ||
@@ -49,3 +50,13 @@ Replace the contents of the editor, but keep | ||
editor.setHTML(value); | ||
editor.setSelection(sel); | ||
if (sel) this.setEditorSelection(editor, sel); | ||
}, | ||
setEditorSelection: function(editor, range) { | ||
if (range) { | ||
// Validate bounds before applying. | ||
var length = editor.getLength(); | ||
range.start = Math.max(0, Math.min(range.start, length-1)); | ||
range.end = Math.max(range.start, Math.min(range.end, length-1)); | ||
} | ||
editor.setSelection(range); | ||
} | ||
@@ -52,0 +63,0 @@ |
@@ -79,4 +79,5 @@ 'use strict'; | ||
renderSeparator: function(item) { | ||
renderSeparator: function(key) { | ||
return React.DOM.span({ | ||
key: key, | ||
className:'ql-format-separator' | ||
@@ -86,5 +87,5 @@ }); | ||
renderGroup: function(item) { | ||
renderGroup: function(item, key) { | ||
return React.DOM.span({ | ||
key: item.label, | ||
key: item.label || key, | ||
className:'ql-format-group' }, | ||
@@ -95,5 +96,5 @@ item.items.map(this.renderItem) | ||
renderChoiceItem: function(item) { | ||
renderChoiceItem: function(item, key) { | ||
return React.DOM.option({ | ||
key: item.label || item.value, | ||
key: item.label || item.value || key, | ||
value:item.value }, | ||
@@ -104,5 +105,5 @@ item.label | ||
renderChoices: function(item) { | ||
renderChoices: function(item, key) { | ||
return React.DOM.select({ | ||
key: item.label, | ||
key: item.label || key, | ||
className: 'ql-'+item.type }, | ||
@@ -113,5 +114,5 @@ item.items.map(this.renderChoiceItem) | ||
renderAction: function(item) { | ||
renderAction: function(item, key) { | ||
return React.DOM.span({ | ||
key: item.label || item.value, | ||
key: item.label || item.value || key, | ||
className: 'ql-format-button ql-'+item.type, | ||
@@ -122,8 +123,8 @@ title: item.label } | ||
renderItem: function(item) { | ||
renderItem: function(item, key) { | ||
switch (item.type) { | ||
case 'separator': | ||
return this.renderSeparator(); | ||
return this.renderSeparator(key); | ||
case 'group': | ||
return this.renderGroup(item); | ||
return this.renderGroup(item, key); | ||
case 'font': | ||
@@ -134,5 +135,5 @@ case 'align': | ||
case 'background': | ||
return this.renderChoices(item); | ||
return this.renderChoices(item, key); | ||
default: | ||
return this.renderAction(item); | ||
return this.renderAction(item, key); | ||
} | ||
@@ -139,0 +140,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21535
393
219