Improve categories tooltip.
authorAlexandre Segura <mex.zktk@gmail.com>
Thu, 08 Jun 2017 18:54:36 +0200
changeset 25 e04714a1d4eb
parent 24 3b3999550508
child 26 930e486ad0a8
Improve categories tooltip. - Manage categories with comment enabled. - Rename mark type to "category". - Simplify code, use only one mark type.
client/src/App.scss
client/src/HtmlSerializer.js
client/src/components/CategoriesTooltip.js
client/src/components/SlateEditor.js
--- a/client/src/App.scss	Thu Jun 08 17:57:57 2017 +0200
+++ b/client/src/App.scss	Thu Jun 08 18:54:36 2017 +0200
@@ -40,7 +40,7 @@
     z-index: 1;
     top: -10000px;
     left: -10000px;
-    margin-top: -64px;
+    margin-top: -20px;
     opacity: 0;
     transition: opacity .75s;
 }
--- a/client/src/HtmlSerializer.js	Thu Jun 08 17:57:57 2017 +0200
+++ b/client/src/HtmlSerializer.js	Thu Jun 08 18:54:36 2017 +0200
@@ -10,7 +10,7 @@
   em: 'italic',
   strong: 'bold',
   u: 'underline',
-  annotation: 'span'
+  category: 'span'
 }
 
 const rules = [
@@ -51,7 +51,7 @@
         case 'bold': return <strong>{children}</strong>
         case 'italic': return <em>{children}</em>
         case 'underline': return <u>{children}</u>
-        case 'annotation': return <span style={{ backgroundColor: object.data.get('color') }}>{children}</span>
+        case 'category': return <span style={{ backgroundColor: object.data.get('color') }}>{children}</span>
         default: return;
       }
     }
--- a/client/src/components/CategoriesTooltip.js	Thu Jun 08 17:57:57 2017 +0200
+++ b/client/src/components/CategoriesTooltip.js	Thu Jun 08 18:54:36 2017 +0200
@@ -3,24 +3,65 @@
 
 class CategoriesTooltip extends Component {
 
+  state = {
+    activeCategory: null,
+    showCommentControl: false
+  }
+
+  componentDidUpdate = () => {
+    if (this.state.showCommentControl) {
+      this.commentControl.focus();
+    }
+  }
+
+  isCategoryActive = (category) => {
+    return this.state.activeCategory && this.state.activeCategory.key === category.key
+  }
+
   onButtonClick = (category) => {
-    if (typeof this.props.onCategoryClick === 'function') {
-      this.props.onCategoryClick(category)
+    if (category.hasOwnProperty('hasComment') && category.hasComment === true) {
+      this.setState({
+        activeCategory: this.state.activeCategory ? null : category,
+        showCommentControl: !this.state.showCommentControl
+      })
+    } else {
+      if (typeof this.props.onCategoryClick === 'function') {
+        this.props.onCategoryClick(category)
+      }
+    }
+  }
+
+  onSubmit = (e) => {
+    e.preventDefault();
+    if (this.state.showCommentControl) {
+      const comment = this.commentControl.value;
+      const { activeCategory } = this.state;
+      Object.assign(activeCategory, { comment: comment });
+      if (typeof this.props.onCategoryClick === 'function') {
+        this.props.onCategoryClick(activeCategory)
+      }
     }
   }
 
   render() {
     return (
       <div className="categories-tooltip">
-        <FormGroup className="buttons">
-        {this.props.categories.map((category) =>
-          <Button key={ category.name } bsStyle="primary" style={{ backgroundColor: category.color }}
-            onClick={this.onButtonClick.bind(this, category)}>{ category.name }</Button>
-        )}
-        </FormGroup>
-        <FormGroup>
-          <FormControl />
-        </FormGroup>
+        <form onSubmit={ this.onSubmit }>
+          <FormGroup className="buttons">
+          {this.props.categories.map((category) =>
+            <Button
+              key={ category.name }
+              bsStyle="primary"
+              style={{ backgroundColor: category.color }}
+              active={ this.isCategoryActive(category)  }
+              onClick={ this.onButtonClick.bind(this, category) }>{ category.name }</Button>
+          )}
+          </FormGroup>
+          {this.state.showCommentControl &&
+          <FormGroup>
+            <FormControl inputRef={(ref) => { this.commentControl = ref; }} />
+          </FormGroup>}
+        </form>
       </div>
     );
   }
--- a/client/src/components/SlateEditor.js	Thu Jun 08 17:57:57 2017 +0200
+++ b/client/src/components/SlateEditor.js	Thu Jun 08 18:54:36 2017 +0200
@@ -31,14 +31,7 @@
     bold: {
       fontWeight: 'bold'
     },
-    // This is a "temporary" mark added when the hovering menu is open
-    highlight: {
-      textDecoration: 'underline',
-      textDecorationStyle: 'dotted',
-      backgroundColor: '#ccc',
-    },
-    // This is the mark actually used for annotations
-    annotation: props => {
+    category: props => {
       const data = props.mark.data;
       return <span style={{ backgroundColor: data.get('color') }}>{props.children}</span>
     },
@@ -54,7 +47,7 @@
 const annotationCategories = [
   { key: 'important', name: 'Important',    color: '#F1C40F' },
   { key: 'keyword',   name: 'Mot-clé',      color: '#2ECC71' },
-  { key: 'comment',   name: 'Commentaire',  color: '#3498DB' }
+  { key: 'comment',   name: 'Commentaire',  color: '#3498DB', hasComment: true }
 ];
 
 /**
@@ -227,25 +220,25 @@
 
   onClickMark = (e, type) => {
     e.preventDefault()
-    let { state, hoveringMenu } = this.state
+    const { state } = this.state
+    const transform = state.transform()
 
-    let toggleMarkOptions;
     let isPortalOpen = false;
 
-    if (type === 'highlight') {
-      toggleMarkOptions = { type: type, data: { text: this.state.currentSelectionText } }
-      isPortalOpen = !this.state.isPortalOpen;
+    if (type === 'category') {
+      // Can't use toggleMark here, because it expects the same object
+      // @see https://github.com/ianstormtaylor/slate/issues/873
+      if (this.hasMark('category')) {
+        state.marks.forEach(mark => transform.removeMark(mark));
+      } else {
+        isPortalOpen = !this.state.isPortalOpen;
+      }
     } else {
-      toggleMarkOptions = type;
+      transform.toggleMark(type)
     }
 
-    state = state
-      .transform()
-      .toggleMark(toggleMarkOptions)
-      .apply()
-
     this.setState({
-      state: state,
+      state: transform.apply(),
       isPortalOpen: isPortalOpen
     })
   }
@@ -314,16 +307,9 @@
   }
 
   onPortalClose = (portal) => {
-
     let { state } = this.state
     const transform = state.transform();
 
-    state.marks.forEach(mark => {
-      if (mark.type === 'highlight') {
-        transform.removeMark(mark)
-      }
-    });
-
     this.setState({
       state: transform.apply(),
       isPortalOpen: false
@@ -336,9 +322,8 @@
     const transform = state.transform();
 
     state.marks.forEach(mark => transform.removeMark(mark));
-
     transform.addMark({
-      type: 'annotation',
+      type: 'category',
       data: {
         text: this.state.currentSelectionText,
         color: category.color,
@@ -350,7 +335,6 @@
       state: transform.apply(),
       isPortalOpen: false
     });
-
   }
 
   /**
@@ -380,7 +364,7 @@
         {this.renderMarkButton('bold', 'format_bold')}
         {this.renderMarkButton('italic', 'format_italic')}
         {this.renderMarkButton('underlined', 'format_underlined')}
-        {this.renderMarkButton('highlight', 'label')}
+        {this.renderMarkButton('category', 'label')}
 
         {this.renderBlockButton('numbered-list', 'format_list_numbered')}
         {this.renderBlockButton('bulleted-list', 'format_list_bulleted')}