From 5b457e1ef73c4be0e66346de743839ee6139a07f Mon Sep 17 00:00:00 2001
From: Alex P <thealexpaxton@gmail.com>
Date: Wed, 20 Sep 2017 12:39:42 -0700
Subject: [PATCH] Redesign context menu to expose actions without dropdown

---
 ui/src/shared/components/ContextMenu.js   |  46 +++---
 ui/src/shared/components/NameableGraph.js |  38 +++--
 ui/src/style/pages/dashboards.scss        | 167 +++++++++-------------
 3 files changed, 96 insertions(+), 155 deletions(-)

diff --git a/ui/src/shared/components/ContextMenu.js b/ui/src/shared/components/ContextMenu.js
index 91d620e5ba..bdf2eba292 100644
--- a/ui/src/shared/components/ContextMenu.js
+++ b/ui/src/shared/components/ContextMenu.js
@@ -1,36 +1,25 @@
 import React, {PropTypes} from 'react'
-import classnames from 'classnames'
 import OnClickOutside from 'react-onclickoutside'
 
 const ContextMenu = OnClickOutside(
-  ({
-    isOpen,
-    isDeleting,
-    toggleMenu,
-    onEdit,
-    onRename,
-    onDelete,
-    onDeleteClick,
-    cell,
-  }) =>
+  ({isDeleting, onEdit, onDeleteClick, onDelete, cell}) =>
     <div
-      className={classnames('dash-graph--options', {
-        'dash-graph--options-show': isOpen || isDeleting,
-      })}
-      onClick={toggleMenu}
+      className={
+        isDeleting
+          ? 'dash-graph-context dash-graph-context__deleting'
+          : 'dash-graph-context'
+      }
     >
-      <button className="btn btn-info btn-xs">
-        <span className="icon caret-down" />
-      </button>
-      <ul className="dash-graph--options-menu">
-        <li onClick={onEdit(cell)}>Edit</li>
-        <li onClick={onRename(cell.x, cell.y, cell.isEditing)}>Rename</li>
-        {isDeleting
-          ? <li className="dash-graph--confirm-delete" onClick={onDelete(cell)}>
-              Confirm Delete
-            </li>
-          : <li onClick={onDeleteClick}>Delete</li>}
-      </ul>
+      <div className="dash-graph-context--button" onClick={onEdit(cell)}>
+        <span className="icon pencil" />
+      </div>
+      {isDeleting
+        ? <div className="dash-graph-context--confirm" onClick={onDelete(cell)}>
+            Confirm
+          </div>
+        : <div className="dash-graph-context--button" onClick={onDeleteClick}>
+            <span className="icon trash" />
+          </div>}
     </div>
 )
 
@@ -45,11 +34,8 @@ const ContextMenuContainer = props => {
 const {bool, func, shape} = PropTypes
 
 ContextMenuContainer.propTypes = {
-  isOpen: bool,
   isDeleting: bool,
-  toggleMenu: func,
   onEdit: func,
-  onRename: func,
   onDelete: func,
   onDeleteClick: func,
   cell: shape(),
diff --git a/ui/src/shared/components/NameableGraph.js b/ui/src/shared/components/NameableGraph.js
index bfc747bc7f..9beb445bca 100644
--- a/ui/src/shared/components/NameableGraph.js
+++ b/ui/src/shared/components/NameableGraph.js
@@ -1,25 +1,19 @@
 import React, {Component, PropTypes} from 'react'
 import _ from 'lodash'
+import classnames from 'classnames'
 
-import NameableGraphHeader from 'shared/components/NameableGraphHeader'
 import ContextMenu from 'shared/components/ContextMenu'
+import CustomTimeIndicator from 'shared/components/CustomTimeIndicator'
 
 class NameableGraph extends Component {
   constructor(props) {
     super(props)
     this.state = {
-      isMenuOpen: false,
       cellName: props.cell.name,
       isDeleting: false,
     }
   }
 
-  toggleMenu = () => {
-    this.setState({
-      isMenuOpen: !this.state.isMenuOpen,
-    })
-  }
-
   handleRenameCell = e => {
     const cellName = e.target.value
     this.setState({cellName})
@@ -33,7 +27,6 @@ class NameableGraph extends Component {
 
   closeMenu = () => {
     this.setState({
-      isMenuOpen: false,
       isDeleting: false,
     })
   }
@@ -51,33 +44,35 @@ class NameableGraph extends Component {
   }
 
   render() {
-    const {cell, children, isEditable, onEditCell, onUpdateCell} = this.props
+    const {cell, children, isEditable, onEditCell} = this.props
 
-    const {cellName, isMenuOpen, isDeleting} = this.state
+    const {cellName, isDeleting} = this.state
     const queries = _.get(cell, ['queries'], [])
 
     return (
       <div className="dash-graph">
-        <NameableGraphHeader
-          cell={cell}
-          cellName={cellName}
-          isEditable={isEditable}
-          onUpdateCell={onUpdateCell}
-          onRenameCell={this.handleRenameCell}
-          onCancelEditCell={this.handleCancelEdit}
-        />
         <ContextMenu
           cell={cell}
           onDeleteClick={this.handleDeleteClick}
           onDelete={this.handleDeleteCell}
           onRename={!cell.isEditing && isEditable ? onEditCell : () => {}}
-          toggleMenu={this.toggleMenu}
           isDeleting={isDeleting}
-          isOpen={isMenuOpen}
           isEditable={isEditable}
           handleClickOutside={this.closeMenu}
           onEdit={this.handleSummonOverlay}
         />
+        <div
+          className={classnames('dash-graph--heading', {
+            'dash-graph--heading-draggable': isEditable,
+          })}
+        >
+          <span className="dash-graph--name">
+            {cellName}
+            {queries && queries.length
+              ? <CustomTimeIndicator queries={queries} />
+              : null}
+          </span>
+        </div>
         <div className="dash-graph--container">
           {queries.length
             ? children
@@ -108,7 +103,6 @@ NameableGraph.propTypes = {
   children: node.isRequired,
   onEditCell: func,
   onRenameCell: func,
-  onUpdateCell: func,
   onDeleteCell: func,
   onSummonOverlayTechnologies: func,
   isEditable: bool,
diff --git a/ui/src/style/pages/dashboards.scss b/ui/src/style/pages/dashboards.scss
index 3ae18fbf35..aa7cf25c47 100644
--- a/ui/src/style/pages/dashboards.scss
+++ b/ui/src/style/pages/dashboards.scss
@@ -142,15 +142,19 @@ $dash-graph-options-arrow: 8px;
   white-space: nowrap;
 }
 .dash-graph--name {
+  position: relative;
   height: $dash-graph-heading;
   line-height: $dash-graph-heading;
-  width: calc(100% - 30px);
-  padding-left: 16px;
+  width: calc(100% - 53px);
+  padding-left: 10px;
   transition:
     color 0.25s ease,
     background-color 0.25s ease,
     border-color 0.25s ease;
 }
+.dash-graph-context__deleting + .dash-graph--heading .dash-graph--name {
+  width: calc(100% - 87px);
+}
 input.form-control.dash-graph--name-edit {
   margin-left: 8px;
   padding: 0 6px;
@@ -171,116 +175,73 @@ input.form-control.dash-graph--name-edit {
   padding: 0 7px;
   position: absolute;
   top: 3px;
-  right: 30px;
-}
-.presentation-mode .dash-graph--custom-time {
   right: 2px;
 }
-.dash-graph--options {
-  width: $dash-graph-heading;
+
+.dash-graph-context {
+  z-index: 2;
   position: absolute;
-  right: 0px;
-  top: 0px;
-  text-align: center;
-
-  > .btn {
-    background-color: transparent !important;
-    padding: 0;
-    margin: 4px 0;
-    height: $dash-graph-heading-context;
-    width: $dash-graph-heading-context;
-    line-height: $dash-graph-heading-context;
-    transition:
-      background-color 0.25s ease,
-      color 0.25s ease !important;
-
-    &:hover {
-      background-color: $g5-pepper !important;
-      color: $g20-white;
-    }
-  }
+  top: 0;
+  right: 3px;
+  height: 30px;
+  display: flex;
+  align-items: center;
+  flex-wrap: nowrap;
 }
+.dash-graph-context--button {
+  width: 24px;
+  height: 24px;
+  border-radius: 3px;
+  font-size: 12px;
+  position: relative;
+  color: $g11-sidewalk;
+  transition:
+    color 0.25s ease,
+    background-color 0.25s ease;
 
-.presentation-mode .dash-graph--options {
-  display: none;
-  visibility: hidden;
-}
-.dash-graph--options-menu {
-  position: absolute;
-  top: ($dash-graph-heading + $dash-graph-options-arrow);
-  left: 50%;
-  transform: translateX(-50%);
-  display: block;
-  z-index: 11;
-  list-style: none;
-  padding: 0;
-  margin: 0;
-  width: auto;
-  visibility: hidden;
-  transition-property: all;
-
-  > li {
-    @include no-user-select;
-    position: relative;
-    width: 100%;
-    height: 28px;
-    line-height: 28px;
-    background-color: $g5-pepper;
-    padding: 0 11px;
-    margin: 0;
-    text-align: left;
-    color: $g15-platinum;
-    white-space: nowrap;
-    opacity: 0;
-    transition:
-      opacity 0.25s ease,
-      color 0.25s ease,
-      background-color 0.25s ease;
-
-    &:first-child {
-      border-radius: $radius $radius 0 0;
-
-      &:before {
-        content: '';
-        width: 0;
-        height: 0;
-        border-width: $dash-graph-options-arrow;
-        border-style: solid;
-        border-color: transparent transparent $g5-pepper transparent;
-        position: absolute;
-        left: 50%;
-        transform: translateX(-50%);
-        top: -($dash-graph-options-arrow * 2);
-      }
-    }
-    &:last-child {
-      border-radius: 0 0 $radius $radius;
-    }
-
-    &:hover {
-      cursor: pointer;
-      background-color: $g7-graphite;
-      color: $g20-white;
-    }
-  }
-}
-.dash-graph--options-menu > li.dash-graph--confirm-delete {
-  color: $c-curacao;
-
-  &:hover {color: $c-dreamsicle;}
-}
-
-/* Menu Open State */
-.dash-graph--options.dash-graph--options-show {
-  > .btn {
+  &:hover {
+    cursor: pointer;
     color: $g20-white;
-    background-color: $g5-pepper !important;
+    background-color: $g5-pepper;
+  }
+  &:first-child {margin-right: 2px;}
+
+  > .icon {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%,-50%);
+  }
+}
+.dash-graph-context--confirm {
+  width: 58px;
+  height: 24px;
+  border-radius: 3px;
+  text-align: center;
+  font-size: 12px;
+  font-weight: 700;
+  line-height: 24px;
+  background-color: $c-curacao;
+  color: $g20-white;
+  transition:
+    color 0.25s ease,
+    background-color 0.25s ease;
+
+  &:hover {
+    background-color: $c-dreamsicle;
+    cursor: pointer;
   }
-  .dash-graph--options-menu { visibility: visible; }
-  .dash-graph--options-menu > li { opacity: 1; }
 }
 
-
+/* Presentation Mode */
+.presentation-mode {
+  .dash-graph-context {
+    display: none;
+  }
+  .dash-graph--name {
+    width: 100%;
+  }
+}
 
 .graph-panel__refreshing {
   position: absolute;