diff --git a/plugins/talk-plugin-rich-text/client/components/rte/RTE.css b/plugins/talk-plugin-rich-text/client/components/rte/RTE.css
index ab24828bd..2bf1feaf5 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/RTE.css
+++ b/plugins/talk-plugin-rich-text/client/components/rte/RTE.css
@@ -16,3 +16,14 @@
margin: 12px 0 0 12px;
color: #bbb;
}
+
+.toolbarDisabled {
+ background: #f8f8f8;
+ cursor: default;
+}
+
+.contentEditableDisabled {
+ background: #fafafa;
+ color: #888;
+ cursor: default;
+}
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/RTE.js b/plugins/talk-plugin-rich-text/client/components/rte/RTE.js
index 422109292..2694c3a26 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/RTE.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/RTE.js
@@ -27,6 +27,7 @@ class RTE extends React.Component {
}
handleChange = () => {
+ this.handleSelectionChange();
this.props.onChange({
text: this.ref.htmlEl.innerText,
html: this.ref.htmlEl.innerHTML,
@@ -60,18 +61,10 @@ class RTE extends React.Component {
return handled;
};
- handleClick = () => {
+ handleMouseUp = () => {
setTimeout(() => this.handleSelectionChange());
};
- handleKeyDown = () => {
- this.handleSelectionChange();
- };
-
- handleKeyUp = () => {
- this.handleSelectionChange();
- };
-
handleKeyPress = e => {
this.handleSelectionChange();
if (e.key === 'Enter') {
@@ -92,36 +85,51 @@ class RTE extends React.Component {
renderButtons() {
return this.props.buttons.map(b => {
return React.cloneElement(b, {
+ disabled: this.props.disabled,
api: this.api,
ref: this.createButtonRefHandler(b.key),
});
});
}
+ getClassNames() {
+ const { disabled } = this.props;
+ return {
+ toolbar: cn(this.props.toolbarClassName, {
+ [this.props.toolbarClassNameDisabled]: disabled,
+ [styles.toolbarDisabled]: disabled,
+ }),
+ content: cn(styles.contentEditable, this.props.contentClassName, {
+ [this.props.contentClassNameDisabled]: disabled,
+ [styles.contentEditableDisabled]: disabled,
+ }),
+ root: cn(this.props.className, {
+ [this.props.classNameDisabled]: disabled,
+ }),
+ placeholder: cn(styles.placeholder, this.props.placeholderClassName, {
+ [this.props.placeholderClassNameDisabled]: disabled,
+ }),
+ };
+ }
+
render() {
- const {
- className,
- contentClassName,
- toolbarClassName,
- value,
- placeholder,
- inputId,
- } = this.props;
+ const { value, placeholder, inputId, disabled } = this.props;
+
+ const classNames = this.getClassNames();
return (
-
-
{this.renderButtons()}
- {!value &&
{placeholder}
}
+
+
{this.renderButtons()}
+ {!value &&
{placeholder}
}
@@ -140,8 +148,13 @@ RTE.propTypes = {
onChange: PropTypes.func,
disabled: PropTypes.bool,
className: PropTypes.string,
+ classNameDisabled: PropTypes.string,
contentClassName: PropTypes.string,
+ contentClassNameDisabled: PropTypes.string,
toolbarClassName: PropTypes.string,
+ toolbarClassNameDisabled: PropTypes.string,
+ placeholderClassName: PropTypes.string,
+ placeholderClassNameDisabled: PropTypes.string,
placeholder: PropTypes.string,
value: PropTypes.string,
};
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/buttons/Blockquote.js b/plugins/talk-plugin-rich-text/client/components/rte/buttons/Blockquote.js
index 7d3b76d6f..d12429339 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/buttons/Blockquote.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/buttons/Blockquote.js
@@ -36,11 +36,11 @@ function execCommand() {
insertNodes(node);
selectNode(node);
}
- this.broadcastChange();
}
+ this.broadcastChange();
}
-function syncState() {
+function isActive() {
return !!findIntersectingTag('BLOCKQUOTE');
}
@@ -52,7 +52,7 @@ const onEnter = node => {
return true;
};
-const Blockquote = createToggle(execCommand, syncState, { onEnter });
+const Blockquote = createToggle(execCommand, { onEnter, isActive });
Blockquote.defaultProps = {
children: 'Blockquote',
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/buttons/Bold.js b/plugins/talk-plugin-rich-text/client/components/rte/buttons/Bold.js
index 4988b0679..7f6259c77 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/buttons/Bold.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/buttons/Bold.js
@@ -1,9 +1,9 @@
import createToggle from '../factories/createToggle';
const execCommand = () => document.execCommand('bold');
-const syncState = () => document.queryCommandState('bold');
+const isActive = () => document.queryCommandState('bold');
-const Bold = createToggle(execCommand, syncState);
+const Bold = createToggle(execCommand, { isActive });
Bold.defaultProps = {
children: 'Bold',
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/buttons/Italic.js b/plugins/talk-plugin-rich-text/client/components/rte/buttons/Italic.js
index 6387e4237..f7fc6a4f4 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/buttons/Italic.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/buttons/Italic.js
@@ -1,9 +1,9 @@
import createToggle from '../factories/createToggle';
const execCommand = () => document.execCommand('italic');
-const syncState = () => document.queryCommandState('italic');
+const isActive = () => document.queryCommandState('italic');
-const Italic = createToggle(execCommand, syncState);
+const Italic = createToggle(execCommand, { isActive });
Italic.defaultProps = {
children: 'Italic',
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/components/Button.css b/plugins/talk-plugin-rich-text/client/components/rte/components/Button.css
index daedd54ec..070906205 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/components/Button.css
+++ b/plugins/talk-plugin-rich-text/client/components/rte/components/Button.css
@@ -35,3 +35,9 @@
border-radius: 3px;
background-color: #ddd;
}
+
+.button:disabled{
+ color: #bbb;
+ cursor: default;
+ background-color: inherit;
+}
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/components/Button.js b/plugins/talk-plugin-rich-text/client/components/rte/components/Button.js
index f7bc75941..f99648cb7 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/components/Button.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/components/Button.js
@@ -12,6 +12,7 @@ class Button extends React.Component {
children,
active,
activeClassName,
+ disabled,
} = this.props;
return (
@@ -34,6 +36,7 @@ Button.propTypes = {
onClick: PropTypes.func,
children: PropTypes.node,
active: PropTypes.bool,
+ disabled: PropTypes.bool,
};
export default Button;
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js b/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js
index 040dc6543..b307ae199 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/factories/createToggle.js
@@ -2,14 +2,19 @@ import React from 'react';
import PropTypes from 'prop-types';
import Button from '../components/Button';
-const createToggle = (execCommand, getCurrentState, { onEnter } = {}) => {
+const createToggle = (
+ execCommand,
+ { onEnter, isActive = () => false, isDisabled = () => false } = {}
+) => {
class Toggle extends React.Component {
state = {
active: false,
+ disabled: false,
};
execCommand = () => execCommand.apply(this.props.api);
- getCurrentState = () => getCurrentState.apply(this.props.api);
+ isActive = () => isActive.apply(this.props.api);
+ isDisabled = () => isDisabled.apply(this.props.api);
onEnter = (...args) => onEnter && onEnter.apply(this.props.api, args);
formatToggle = () => {
@@ -23,11 +28,16 @@ const createToggle = (execCommand, getCurrentState, { onEnter } = {}) => {
};
syncState = () => {
- if (this.state.active !== this.getCurrentState()) {
+ if (this.state.active !== this.isActive()) {
this.setState(state => ({
active: !state.active,
}));
}
+ if (this.state.disabled !== this.isDisabled()) {
+ this.setState(state => ({
+ disabled: !state.disabled,
+ }));
+ }
};
onSelectionChange() {
@@ -35,13 +45,14 @@ const createToggle = (execCommand, getCurrentState, { onEnter } = {}) => {
}
render() {
- const { className, title, children } = this.props;
+ const { className, title, children, disabled } = this.props;
return (
@@ -54,6 +65,7 @@ const createToggle = (execCommand, getCurrentState, { onEnter } = {}) => {
className: PropTypes.string,
title: PropTypes.string,
children: PropTypes.node,
+ disabled: PropTypes.bool,
};
return Toggle;
};
diff --git a/plugins/talk-plugin-rich-text/client/components/rte/lib/dom.js b/plugins/talk-plugin-rich-text/client/components/rte/lib/dom.js
index 24a22f317..e611de234 100644
--- a/plugins/talk-plugin-rich-text/client/components/rte/lib/dom.js
+++ b/plugins/talk-plugin-rich-text/client/components/rte/lib/dom.js
@@ -118,11 +118,24 @@ function ensureEndMarker(node) {
}
}
+export function nodeContains(node, lookFor) {
+ if (node.isSameNode(lookFor)) {
+ return true;
+ }
+ for (let i = 0; i < node.childNodes.length; i++) {
+ if (nodeContains(node.childNodes[i], lookFor)) {
+ return true;
+ }
+ }
+ return false;
+}
+
export function selectionIsInside(node) {
const range = getSelectionRange();
return (
range &&
- (node.contains(range.startContainer) || node.contains(range.endContainer))
+ (nodeContains(node, range.startContainer) ||
+ nodeContains(node, range.endContainer))
);
}
@@ -251,6 +264,9 @@ export function getWholeLine(node) {
return [node];
}
const child = lastParentBeforeBlock(node);
+ if (child.tagName === 'BR') {
+ return [...getLeftOfNode(child), child];
+ }
return [...getLeftOfNode(child), child, ...getRightOfNode(child)];
}
@@ -302,13 +318,13 @@ export function getSelectedChildren(ancestor) {
for (let i = 0; i < ancestor.childNodes.length; i++) {
const node = ancestor.childNodes[i];
if (!foundStart) {
- if (node.contains(start)) {
+ if (nodeContains(node, start)) {
foundStart = true;
}
}
if (foundStart) {
result.push(node);
- if (node.contains(end)) {
+ if (nodeContains(node, end)) {
break;
}
}