From f1c58efebb168566f3e435a0b8b8ab53c08b7a59 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 7 Apr 2016 14:01:58 +0200 Subject: [PATCH 1/5] Fixes url state decoding in Firefox. --- client/app/scripts/utils/router-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/scripts/utils/router-utils.js b/client/app/scripts/utils/router-utils.js index 6878b5ac6..df39d11d8 100644 --- a/client/app/scripts/utils/router-utils.js +++ b/client/app/scripts/utils/router-utils.js @@ -19,7 +19,7 @@ export function updateRoute() { const urlStateString = window.location.hash .replace('#!/state/', '') .replace('#!/', '') || '{}'; - const prevState = JSON.parse(urlStateString); + const prevState = JSON.parse(decodeURIComponent(urlStateString)); if (shouldReplaceState(prevState, state)) { // Replace the top of the history rather than pushing on a new item. From c452b68200bc610588d46fa0403013c6166e98a6 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 7 Apr 2016 14:02:19 +0200 Subject: [PATCH 2/5] Fixes hotkeys in firefox and all browsers + international keyboards. --- client/app/scripts/components/app.js | 37 +++++++++++++++++++--------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index da50a2acb..1b00cc961 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -22,10 +22,6 @@ import { showingDebugToolbar, toggleDebugToolbar, DebugToolbar } from './debug-toolbar.js'; const ESC_KEY_CODE = 27; -const D_KEY_CODE = 68; -const Q_KEY_CODE = 81; -const RIGHT_ANGLE_KEY_IDENTIFIER = 'U+003C'; -const LEFT_ANGLE_KEY_IDENTIFIER = 'U+003E'; const keyPressLog = debug('scope:app-key-press'); /* make sure these can all be shallow-checked for equality for PureRenderMixin */ @@ -65,12 +61,14 @@ export default class App extends React.Component { super(props, context); this.onChange = this.onChange.bind(this); this.onKeyPress = this.onKeyPress.bind(this); + this.onKeyUp = this.onKeyUp.bind(this); this.state = getStateFromStores(); } componentDidMount() { AppStore.addListener(this.onChange); - window.addEventListener('keyup', this.onKeyPress); + window.addEventListener('keypress', this.onKeyPress); + window.addEventListener('keyup', this.onKeyUp); getRouter().start({hashbang: true}); if (!AppStore.isRouteSet()) { @@ -80,22 +78,39 @@ export default class App extends React.Component { getApiDetails(); } + componentWillUnmount() { + window.removeEventListener('keypress', this.onKeyPress); + window.removeEventListener('keyup', this.onKeyUp); + } + onChange() { this.setState(getStateFromStores()); } - onKeyPress(ev) { - keyPressLog('onKeyPress', 'keyCode', ev.keyCode, ev); + onKeyUp(ev) { + // don't get esc in onKeyPress if (ev.keyCode === ESC_KEY_CODE) { hitEsc(); - } else if (ev.keyIdentifier === RIGHT_ANGLE_KEY_IDENTIFIER) { + } + } + + onKeyPress(ev) { + // + // keyup gives 'key' + // keypress gives 'char' + // Distinction is important for international keyboard layouts where there + // is often a different {key: char} mapping. + // + keyPressLog('onKeyPress', 'keyCode', ev.keyCode, ev); + const char = String.fromCharCode(ev.charCode); + if (char === '<') { pinNextMetric(-1); - } else if (ev.keyIdentifier === LEFT_ANGLE_KEY_IDENTIFIER) { + } else if (char === '>') { pinNextMetric(1); - } else if (ev.keyCode === Q_KEY_CODE) { + } else if (char === 'q') { unpinMetric(); selectMetric(null); - } else if (ev.keyCode === D_KEY_CODE) { + } else if (char === 'd') { toggleDebugToolbar(); this.forceUpdate(); } From 0a9f26bb60e7a505bee1ccfe6a9fc7f293829c2c Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Thu, 7 Apr 2016 18:33:34 +0200 Subject: [PATCH 3/5] Adds a shortcut panel! --- client/app/scripts/actions/app-actions.js | 20 ++++++- client/app/scripts/components/app.js | 9 ++- client/app/scripts/components/help-panel.js | 43 +++++++++++++++ client/app/scripts/constants/action-types.js | 4 +- client/app/scripts/stores/app-store.js | 15 +++++ client/app/styles/main.less | 58 ++++++++++++++++++++ 6 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 client/app/scripts/components/help-panel.js diff --git a/client/app/scripts/actions/app-actions.js b/client/app/scripts/actions/app-actions.js index 36274ce29..61327d4bb 100644 --- a/client/app/scripts/actions/app-actions.js +++ b/client/app/scripts/actions/app-actions.js @@ -13,6 +13,22 @@ import AppStore from '../stores/app-store'; const log = debug('scope:app-actions'); +export function showHelp() { + AppDispatcher.dispatch({type: ActionTypes.SHOW_HELP}); +} + +export function hideHelp() { + AppDispatcher.dispatch({type: ActionTypes.HIDE_HELP}); +} + +export function toggleHelp() { + if (AppStore.getShowingHelp()) { + hideHelp(); + } else { + showHelp(); + } +} + export function selectMetric(metricId) { AppDispatcher.dispatch({ type: ActionTypes.SELECT_METRIC, @@ -219,7 +235,9 @@ export function enterNode(nodeId) { export function hitEsc() { const controlPipe = AppStore.getControlPipe(); - if (controlPipe && controlPipe.get('status') === 'PIPE_DELETED') { + if (AppStore.getShowingHelp()) { + hideHelp(); + } else if (controlPipe && controlPipe.get('status') === 'PIPE_DELETED') { AppDispatcher.dispatch({ type: ActionTypes.CLICK_CLOSE_TERMINAL, pipeId: controlPipe.get('id') diff --git a/client/app/scripts/components/app.js b/client/app/scripts/components/app.js index 1b00cc961..ab9babe27 100644 --- a/client/app/scripts/components/app.js +++ b/client/app/scripts/components/app.js @@ -7,12 +7,13 @@ import Logo from './logo'; import AppStore from '../stores/app-store'; import Footer from './footer.js'; import Sidebar from './sidebar.js'; +import HelpPanel from './help-panel'; import Status from './status.js'; import Topologies from './topologies.js'; import TopologyOptions from './topology-options.js'; import { getApiDetails, getTopologies } from '../utils/web-api-utils'; import { pinNextMetric, hitEsc, unpinMetric, - selectMetric } from '../actions/app-actions'; + selectMetric, toggleHelp } from '../actions/app-actions'; import Details from './details'; import Nodes from './nodes'; import MetricSelector from './metric-selector'; @@ -43,6 +44,7 @@ function getStateFromStores() { availableCanvasMetrics: AppStore.getAvailableCanvasMetrics(), nodeDetails: AppStore.getNodeDetails(), nodes: AppStore.getNodes(), + showingHelp: AppStore.getShowingHelp(), selectedNodeId: AppStore.getSelectedNodeId(), selectedMetric: AppStore.getSelectedMetric(), topologies: AppStore.getTopologies(), @@ -113,6 +115,8 @@ export default class App extends React.Component { } else if (char === 'd') { toggleDebugToolbar(); this.forceUpdate(); + } else if (char === '?') { + toggleHelp(); } } @@ -127,6 +131,9 @@ export default class App extends React.Component { return (
{showingDebugToolbar() && } + + {this.state.showingHelp && } + {showingDetails &&
} diff --git a/client/app/scripts/components/help-panel.js b/client/app/scripts/components/help-panel.js new file mode 100644 index 000000000..3c28914e1 --- /dev/null +++ b/client/app/scripts/components/help-panel.js @@ -0,0 +1,43 @@ +import React from 'react'; + +const GENERAL_SHORTCUTS = [ + {key: 'esc', label: 'Close panel'}, +]; + +const CANVAS_METRIC_SHORTCUTS = [ + {key: '<', label: 'Pin previous metric'}, + {key: '>', label: 'Pin next metric'}, + {key: 'q', label: 'Unpin current metric'}, +]; + +function renderShortcuts(cuts) { + return ( +
+ {cuts.map(({key, label}) => ( +
+
{key}
+
{label}
+
+ ))} +
+ ); +} + +export default class HelpPanel extends React.Component { + render() { + return ( +
+
+

Keyboard Shortcuts

+
+
+

General

+ {renderShortcuts(GENERAL_SHORTCUTS)} +

Canvas Metrics

+ {renderShortcuts(CANVAS_METRIC_SHORTCUTS)} +
+
+ ); + } +} + diff --git a/client/app/scripts/constants/action-types.js b/client/app/scripts/constants/action-types.js index a6b3f5912..b6290f2fb 100644 --- a/client/app/scripts/constants/action-types.js +++ b/client/app/scripts/constants/action-types.js @@ -21,6 +21,7 @@ const ACTION_TYPES = [ 'DO_CONTROL_SUCCESS', 'ENTER_EDGE', 'ENTER_NODE', + 'HIDE_HELP', 'LEAVE_EDGE', 'LEAVE_NODE', 'PIN_METRIC', @@ -36,7 +37,8 @@ const ACTION_TYPES = [ 'RECEIVE_API_DETAILS', 'RECEIVE_ERROR', 'ROUTE_TOPOLOGY', - 'SELECT_METRIC' + 'SELECT_METRIC', + 'SHOW_HELP' ]; export default _.zipObject(ACTION_TYPES, ACTION_TYPES); diff --git a/client/app/scripts/stores/app-store.js b/client/app/scripts/stores/app-store.js index 9f11ac536..646e11f10 100644 --- a/client/app/scripts/stores/app-store.js +++ b/client/app/scripts/stores/app-store.js @@ -58,6 +58,7 @@ let routeSet = false; let controlPipes = makeOrderedMap(); // pipeId -> controlPipe let updatePausedAt = null; // Date let websocketClosed = true; +let showingHelp = false; let selectedMetric = null; let pinnedMetric = selectedMetric; @@ -149,6 +150,10 @@ export class AppStore extends Store { }; } + getShowingHelp() { + return showingHelp; + } + getActiveTopologyOptions() { // options for current topology, sub-topologies share options with parent if (currentTopology && currentTopology.get('parentId')) { @@ -449,6 +454,16 @@ export class AppStore extends Store { this.__emitChange(); break; } + case ActionTypes.SHOW_HELP: { + showingHelp = true; + this.__emitChange(); + break; + } + case ActionTypes.HIDE_HELP: { + showingHelp = false; + this.__emitChange(); + break; + } case ActionTypes.DESELECT_NODE: { closeNodeDetails(); this.__emitChange(); diff --git a/client/app/styles/main.less b/client/app/styles/main.less index fe9d46fe1..d3f815c07 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -1080,6 +1080,64 @@ h2 { } } +// +// Help panel! +// + +.help-panel { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 2048; + .shadow-2; + + &-header { + background-color: @weave-blue; + padding: 36px; + color: white; + + h2 { + margin: 0; + } + } + + &-main { + padding: 12px 36px 36px; + background-color: white; + } + + h3 { + text-transform: uppercase; + font-size: 90%; + color: #8383ac; + padding: 4px 0; + } + + &-shortcut { + kbd { + display: inline-block; + padding: 3px 5px; + font-size: 11px; + line-height: 10px; + color: #555; + vertical-align: middle; + background-color: #fcfcfc; + border: solid 1px #ccc; + border-bottom-color: #bbb; + border-radius: 3px; + box-shadow: inset 0 -1px 0 #bbb; + } + div.key { + width: 100px; + display: inline-block; + } + div.label { + display: inline-block; + } + } +} + // // Debug panel! // From a90b8f6e71acb4940ab5e9c79e596bb128a0c47c Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Mon, 11 Apr 2016 09:54:26 +0200 Subject: [PATCH 4/5] Adds '?' to shortcuts - Fixes '?' after using the terminal - Fixes styling, border-radius was behaving strangley w/ transform to center the overlay, so opted for more old school methods. --- client/app/scripts/components/help-panel.js | 7 ++++--- client/app/scripts/components/terminal.js | 1 + client/app/styles/main.less | 10 ++++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/client/app/scripts/components/help-panel.js b/client/app/scripts/components/help-panel.js index 3c28914e1..42564a249 100644 --- a/client/app/scripts/components/help-panel.js +++ b/client/app/scripts/components/help-panel.js @@ -1,12 +1,13 @@ import React from 'react'; const GENERAL_SHORTCUTS = [ - {key: 'esc', label: 'Close panel'}, + {key: 'esc', label: 'Close active panel'}, + {key: '?', label: 'Toggle shortcut menu'}, ]; const CANVAS_METRIC_SHORTCUTS = [ - {key: '<', label: 'Pin previous metric'}, - {key: '>', label: 'Pin next metric'}, + {key: '<', label: 'Select and pin previous metric'}, + {key: '>', label: 'Select and pin next metric'}, {key: 'q', label: 'Unpin current metric'}, ]; diff --git a/client/app/scripts/components/terminal.js b/client/app/scripts/components/terminal.js index 591a5d59a..ff062659e 100644 --- a/client/app/scripts/components/terminal.js +++ b/client/app/scripts/components/terminal.js @@ -175,6 +175,7 @@ export default class Terminal extends React.Component { if (this.term) { log('destroy terminal'); + this.term.blur(); this.term.destroy(); this.term = null; } diff --git a/client/app/styles/main.less b/client/app/styles/main.less index d3f815c07..35b49aa91 100644 --- a/client/app/styles/main.less +++ b/client/app/styles/main.less @@ -1084,12 +1084,19 @@ h2 { // Help panel! // +@help-panel-width: 400px; +@help-panel-height: 380px; .help-panel { position: absolute; + -webkit-transform: translate3d(0, 0, 0); top: 50%; left: 50%; - transform: translate(-50%, -50%); + width: @help-panel-width; + height: @help-panel-height; + margin-left: @help-panel-width / -2; + margin-top: @help-panel-height / -2; z-index: 2048; + background-color: white; .shadow-2; &-header { @@ -1104,7 +1111,6 @@ h2 { &-main { padding: 12px 36px 36px; - background-color: white; } h3 { From bb891b0ffa4a1e8127d3a10890badb4c5bc9c530 Mon Sep 17 00:00:00 2001 From: Simon Howe Date: Mon, 11 Apr 2016 10:00:24 +0200 Subject: [PATCH 5/5] Adds '?' button down to bottom to toggle shortcut overlay --- client/app/scripts/components/footer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/app/scripts/components/footer.js b/client/app/scripts/components/footer.js index 590c7db01..904b22018 100644 --- a/client/app/scripts/components/footer.js +++ b/client/app/scripts/components/footer.js @@ -4,7 +4,7 @@ import moment from 'moment'; import { getUpdateBufferSize } from '../utils/update-buffer-utils'; import { contrastModeUrl, isContrastMode } from '../utils/contrast-utils'; import { clickDownloadGraph, clickForceRelayout, clickPauseUpdate, - clickResumeUpdate } from '../actions/app-actions'; + clickResumeUpdate, toggleHelp } from '../actions/app-actions'; import { basePathSlash } from '../utils/web-api-utils'; export default function Footer(props) { @@ -64,6 +64,9 @@ export default function Footer(props) { + + +