Skip to content

Commit

Permalink
fix(xod-client): move Editor hotkeys to the App component, focus …
Browse files Browse the repository at this point in the history
…on `App` on start and auto-focus to the `App` from body element
  • Loading branch information
brusherru committed Nov 6, 2020
1 parent 136c2e9 commit 202b3f8
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 36 deletions.
12 changes: 7 additions & 5 deletions packages/xod-client-browser/src/containers/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ class App extends client.App {

this.hideInstallAppPopup = this.hideInstallAppPopup.bind(this);

this.hotkeyHandlers = {
[client.COMMAND.NEW_PROJECT]: this.onCreateProject,
[client.COMMAND.UNDO]: this.props.actions.undoCurrentPatch,
[client.COMMAND.REDO]: this.props.actions.redoCurrentPatch,
};
this.hotkeyHandlers = R.merge(
{
[client.COMMAND.NEW_PROJECT]: this.onCreateProject,
[client.COMMAND.ADD_PATCH]: this.props.actions.createPatch,
},
this.defaultHotkeyHandlers
);

this.urlActions = {
[client.URL_ACTION_TYPES.OPEN_TUTORIAL]: this.onOpenTutorial,
Expand Down
5 changes: 1 addition & 4 deletions packages/xod-client-electron/src/view/containers/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,7 @@ class App extends client.App {
this.showError(error);
});

this.hotkeyHandlers = {
[client.COMMAND.UNDO]: this.props.actions.undoCurrentPatch,
[client.COMMAND.REDO]: this.props.actions.redoCurrentPatch,
};
this.hotkeyHandlers = this.defaultHotkeyHandlers;

this.urlActions = {
// actionPathName: params => this.props.actions.someAction(params.foo, params.bar),
Expand Down
54 changes: 51 additions & 3 deletions packages/xod-client/src/core/containers/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import {
LIVENESS,
} from 'xod-arduino';

import { isInputTarget } from '../../utils/browser';
import { isInputTarget, elementHasFocusFunction } from '../../utils/browser';
import {
lowercaseKebabMask,
patchBasenameMask,
Expand All @@ -58,6 +58,7 @@ import {
import { USERNAME_NEEDED_FOR_LITERAL } from '../../user/messages';
import { PROJECT_NAME_NEEDED_FOR_LITERAL } from '../../project/messages';
import { DO_NOT_USE_TETHERING_INTERNET_IN_BROWSER } from '../../debugger/messages';
import { COMMAND } from '../../utils/constants';

import formatErrorMessage from '../formatErrorMessage';

Expand All @@ -72,6 +73,19 @@ export default class App extends React.Component {
);
this.getGlobals = this.getGlobals.bind(this);

this.defaultHotkeyHandlers = {
[COMMAND.UNDO]: this.props.actions.undoCurrentPatch,
[COMMAND.REDO]: this.props.actions.redoCurrentPatch,
[COMMAND.HIDE_HELPBOX]: this.props.actions.hideHelpbox,
[COMMAND.TOGGLE_HELP]: this.props.actions.toggleHelp,
[COMMAND.INSERT_NODE]: event => {
if (isInputTarget(event)) return;
this.props.actions.showSuggester(null);
},
[COMMAND.PAN_TO_ORIGIN]: this.props.actions.panToOrigin,
[COMMAND.PAN_TO_CENTER]: this.props.actions.panToCenter,
};

/**
* We have to handle some hotkeys, because:
* - Browser IDE should prevent default event handling
Expand All @@ -96,6 +110,33 @@ export default class App extends React.Component {
document.addEventListener('copy', this.props.actions.copyEntities);
document.addEventListener('paste', this.props.actions.pasteEntities);
this.props.actions.fetchGrant(/* startup */ true);

// TODO: Replace with ref and `React.createRef`
// after updating React >16.3
const appEl = document.getElementById('App');

if (elementHasFocusFunction(appEl)) appEl.focus();

/**
* Listen capturing focusout on any element inside body element
* and if element unfocused to the body element — focus on the `App`
* component to make sure main hotkeys will work anytime.
*/
document.body.addEventListener(
'focusout',
// We need a timeout here to await while focus changed on the some element
() =>
setTimeout(() => {
// If focused changed to the body element — focus to the App
if (
document.activeElement === document.body &&
elementHasFocusFunction(appEl)
) {
appEl.focus();
}
}, 0),
true
);
}

onShowCodeArduino(liveness = LIVENESS.NONE) {
Expand Down Expand Up @@ -309,7 +350,7 @@ export default class App extends React.Component {
}

render() {
return <div />;
return <div id="App" />;
}
}

Expand Down Expand Up @@ -353,6 +394,7 @@ App.propTypes = {
toggleDebugger: PropTypes.func.isRequired,
logDebugger: PropTypes.func.isRequired,
clearDebugger: PropTypes.func.isRequired,
showSuggester: PropTypes.func.isRequired,
showLibSuggester: PropTypes.func.isRequired,
toggleAccountPane: PropTypes.func.isRequired,
fetchGrant: PropTypes.func.isRequired,
Expand All @@ -362,6 +404,9 @@ App.propTypes = {
abortSimulation: PropTypes.func.isRequired,
generateApiKey: PropTypes.func.isRequired,
renewApiToken: PropTypes.func.isRequired,
hideHelpbox: PropTypes.func.isRequired,
panToOrigin: PropTypes.func.isRequired,
panToCenter: PropTypes.func.isRequired,
/* eslint-enable react/no-unused-prop-types */
}),
};
Expand Down Expand Up @@ -394,14 +439,14 @@ App.actions = {
startSerialSession: actions.startSerialSession,
stopDebuggerSession: actions.stopDebuggerSession,
toggleDebugger: actions.toggleDebugger,
showSuggester: actions.showSuggester,
logDebugger: actions.addMessagesToDebuggerLog,
clearDebugger: actions.clearDebuggerLog,
cutEntities: actions.cutEntities,
copyEntities: actions.copyEntities,
pasteEntities: actions.pasteEntities,
setCurrentPatchOffsetToOrigin: actions.setCurrentPatchOffsetToOrigin,
setCurrentPatchOffsetToCenter: actions.setCurrentPatchOffsetToCenter,
showSuggester: actions.showSuggester,
showLibSuggester: actions.showLibSuggester,
toggleAccountPane: actions.toggleAccountPane,
fetchGrant: actions.fetchGrant,
Expand All @@ -412,4 +457,7 @@ App.actions = {
abortSimulation: actions.abortSimulation,
generateApiKey: actions.generateApiKey,
renewApiToken: actions.renewApiToken,
hideHelpbox: actions.hideHelpbox,
panToOrigin: actions.setCurrentPatchOffsetToOrigin,
panToCenter: actions.setCurrentPatchOffsetToCenter,
};
28 changes: 4 additions & 24 deletions packages/xod-client/src/editor/containers/Editor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DragDropContext } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { HotKeys, FocusTrap } from 'react-hotkeys';
import { FocusTrap } from 'react-hotkeys';
import * as XP from 'xod-project';
import debounce from 'throttle-debounce/debounce';

Expand All @@ -19,7 +19,6 @@ import * as DebuggerSelectors from '../../debugger/selectors';
import * as EditorSelectors from '../selectors';

import { isInputTarget } from '../../utils/browser';
import { COMMAND } from '../../utils/constants';
import sanctuaryPropType from '../../utils/sanctuaryPropType';
import { FOCUS_AREAS, TAB_TYPES, SIDEBAR_IDS } from '../constants';

Expand All @@ -45,7 +44,6 @@ class Editor extends React.Component {
constructor(props) {
super(props);

this.getHotkeyHandlers = this.getHotkeyHandlers.bind(this);
this.toggleHelp = this.toggleHelp.bind(this);
this.onAddNode = this.onAddNode.bind(this);
this.onInstallLibrary = this.onInstallLibrary.bind(this);
Expand Down Expand Up @@ -84,19 +82,6 @@ class Editor extends React.Component {
this.props.actions.setFocusedArea(FOCUS_AREAS.LIB_SUGGESTER);
}

getHotkeyHandlers() {
return {
[COMMAND.HIDE_HELPBOX]: () => this.props.actions.hideHelpbox(),
[COMMAND.TOGGLE_HELP]: this.toggleHelp,
[COMMAND.INSERT_NODE]: event => {
if (isInputTarget(event)) return;
this.showSuggester(null);
},
[COMMAND.PAN_TO_ORIGIN]: this.props.actions.panToOrigin,
[COMMAND.PAN_TO_CENTER]: this.props.actions.panToCenter,
};
}

toggleHelp(e) {
if (isInputTarget(e)) return;

Expand Down Expand Up @@ -261,8 +246,7 @@ class Editor extends React.Component {
)(this.props.panelsSettings);

return (
<HotKeys
handlers={this.getHotkeyHandlers()}
<div
className={cn('Editor', {
leftSidebarMaximized: areSidebarsMaximized[SIDEBAR_IDS.LEFT],
rightSidebarMaximized: areSidebarsMaximized[SIDEBAR_IDS.RIGHT],
Expand Down Expand Up @@ -301,7 +285,7 @@ class Editor extends React.Component {
onAutohideClick={this.props.actions.togglePanelAutohide}
/>
<Tooltip />
</HotKeys>
</div>
);
}
}
Expand Down Expand Up @@ -342,8 +326,6 @@ Editor.propTypes = {
togglePanelAutohide: PropTypes.func.isRequired,
hideHelpbox: PropTypes.func.isRequired,
showHelpbox: PropTypes.func.isRequired,
panToOrigin: PropTypes.func.isRequired,
panToCenter: PropTypes.func.isRequired,
runTabtest: PropTypes.func.isRequired,
}),
};
Expand Down Expand Up @@ -387,10 +369,8 @@ const mapDispatchToProps = dispatch => ({
minimizePanel: Actions.minimizePanel,
movePanel: Actions.movePanel,
togglePanelAutohide: Actions.togglePanelAutohide,
hideHelpbox: Actions.hideHelpbox,
showHelpbox: Actions.showHelpbox,
panToOrigin: Actions.setCurrentPatchOffsetToOrigin,
panToCenter: Actions.setCurrentPatchOffsetToCenter,
hideHelpbox: Actions.hideHelpbox,
runTabtest: Actions.runTabtest,
},
dispatch
Expand Down
3 changes: 3 additions & 0 deletions packages/xod-client/src/utils/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,6 @@ export const isMacOS = () => window.navigator.appVersion.indexOf('Mac') !== -1;
export const restoreFocusOnApp = () => {
document.getElementById('App').focus();
};

export const elementHasFocusFunction = el =>
el && typeof el.focus === 'function';

0 comments on commit 202b3f8

Please sign in to comment.