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;