mirror of
https://github.com/Kong/insomnia.git
synced 2026-04-21 22:57:59 -04:00
Merge branch 'develop' of github.com:getinsomnia/insomnia into develop
This commit is contained in:
265
app/common/hotkeys.js
Normal file
265
app/common/hotkeys.js
Normal file
@@ -0,0 +1,265 @@
|
||||
// @flow
|
||||
import keycodes from './keycodes';
|
||||
import {isMac} from './constants';
|
||||
import {trackEvent} from '../analytics/index';
|
||||
|
||||
export type Hotkey = {
|
||||
description: string,
|
||||
meta: boolean,
|
||||
alt: boolean,
|
||||
shift: boolean,
|
||||
keycode: number | Array<number>
|
||||
};
|
||||
|
||||
export const SHOW_WORKSPACE_SETTINGS: Hotkey = {
|
||||
description: 'Show Workspace Settings',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.comma
|
||||
};
|
||||
|
||||
export const SHOW_REQUEST_SETTINGS: Hotkey = {
|
||||
description: 'Show Request Settings',
|
||||
meta: true,
|
||||
alt: true,
|
||||
shift: true,
|
||||
keycode: keycodes.comma
|
||||
};
|
||||
|
||||
export const SHOW_SETTINGS: Hotkey = {
|
||||
description: 'Show App Preferences',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.comma
|
||||
};
|
||||
|
||||
export const TOGGLE_MAIN_MENU: Hotkey = {
|
||||
description: 'Toggle Main Menu',
|
||||
meta: true,
|
||||
alt: true,
|
||||
shift: false,
|
||||
keycode: keycodes.comma
|
||||
};
|
||||
|
||||
export const TOGGLE_SIDEBAR: Hotkey = {
|
||||
description: 'Toggle Sidebar',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.backslash
|
||||
};
|
||||
|
||||
export const SHOW_QUICK_SWITCHER: Hotkey = {
|
||||
description: 'Switch Requests',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.p
|
||||
};
|
||||
|
||||
export const SHOW_AUTOCOMPLETE: Hotkey = {
|
||||
description: 'Show Autocomplete',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.space
|
||||
};
|
||||
|
||||
export const SEND_REQUEST: Hotkey = {
|
||||
description: 'Send Request',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: [keycodes.enter, keycodes.r]
|
||||
};
|
||||
|
||||
export const SEND_REQUEST_F5: Hotkey = {
|
||||
description: 'Send Request',
|
||||
meta: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.f5
|
||||
};
|
||||
|
||||
export const SHOW_SEND_OPTIONS: Hotkey = {
|
||||
description: 'Send Request (Options)',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.enter
|
||||
};
|
||||
|
||||
export const SHOW_ENVIRONMENTS: Hotkey = {
|
||||
description: 'Show Environment Editor',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.e
|
||||
};
|
||||
|
||||
export const TOGGLE_ENVIRONMENTS_MENU: Hotkey = {
|
||||
description: 'Switch Environments',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.e
|
||||
};
|
||||
|
||||
export const TOGGLE_METHOD_DROPDOWN: Hotkey = {
|
||||
description: 'Change HTTP Method',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.l
|
||||
};
|
||||
|
||||
export const TOGGLE_HISTORY_DROPDOWN: Hotkey = {
|
||||
description: 'Show Request History',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.h
|
||||
};
|
||||
|
||||
export const FOCUS_URL: Hotkey = {
|
||||
description: 'Focus URL',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.l
|
||||
};
|
||||
|
||||
export const GENERATE_CODE: Hotkey = {
|
||||
description: 'Generate Code',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.g
|
||||
};
|
||||
|
||||
export const FOCUS_FILTER: Hotkey = {
|
||||
description: 'Filter Sidebar',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.f
|
||||
};
|
||||
|
||||
export const SHOW_COOKIES: Hotkey = {
|
||||
description: 'Edit Cookies',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.k
|
||||
};
|
||||
|
||||
export const CREATE_REQUEST: Hotkey = {
|
||||
description: 'Create Request',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.n
|
||||
};
|
||||
|
||||
export const CREATE_FOLDER: Hotkey = {
|
||||
description: 'Create Folder',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: true,
|
||||
keycode: keycodes.n
|
||||
};
|
||||
|
||||
export const DUPLICATE_REQUEST: Hotkey = {
|
||||
description: 'Duplicate Request',
|
||||
meta: true,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.d
|
||||
};
|
||||
|
||||
export const CLOSE_DROPDOWN: Hotkey = {
|
||||
description: 'Close Dropdown',
|
||||
meta: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.esc
|
||||
};
|
||||
|
||||
export const CLOSE_MODAL: Hotkey = {
|
||||
description: 'Close Modal',
|
||||
meta: false,
|
||||
alt: false,
|
||||
shift: false,
|
||||
keycode: keycodes.esc
|
||||
};
|
||||
|
||||
export function pressedHotKey (e: KeyboardEvent, definition: Hotkey): boolean {
|
||||
const isMetaPressed = isMac() ? e.metaKey : e.ctrlKey;
|
||||
const isAltPressed = isMac() ? e.ctrlKey : e.altKey;
|
||||
const isShiftPressed = e.shiftKey;
|
||||
|
||||
const {meta, alt, shift, keycode} = definition;
|
||||
const codes = Array.isArray(keycode) ? keycode : [keycode];
|
||||
|
||||
for (const code of codes) {
|
||||
if ((alt && !isAltPressed) || (!alt && isAltPressed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((meta && !isMetaPressed) || (!meta && isMetaPressed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((shift && !isShiftPressed) || (!shift && isShiftPressed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (code !== e.keyCode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function executeHotKey (
|
||||
e: KeyboardEvent,
|
||||
definition: Hotkey,
|
||||
callback: Function
|
||||
): void {
|
||||
if (pressedHotKey(e, definition)) {
|
||||
callback();
|
||||
trackEvent('Hotkey', definition.description);
|
||||
}
|
||||
}
|
||||
|
||||
export function getChar (hotkey: Hotkey) {
|
||||
const codes = Array.isArray(hotkey.keycode) ? hotkey.keycode : [hotkey.keycode];
|
||||
const chars = [];
|
||||
|
||||
for (const keycode of codes) {
|
||||
const v = Object.keys(keycodes).find(k => keycodes[k] === keycode);
|
||||
|
||||
if (!v) {
|
||||
console.error('Invalid hotkey', hotkey);
|
||||
} else if (v.toUpperCase() === 'ENTER') {
|
||||
chars.push('Enter');
|
||||
} else if (v.toUpperCase() === 'COMMA') {
|
||||
chars.push(',');
|
||||
} else if (v.toUpperCase() === 'BACKSLASH') {
|
||||
chars.push('\\');
|
||||
} else if (v.toUpperCase() === 'FORWARDSLASH') {
|
||||
chars.push('/');
|
||||
} else if (v.toUpperCase() === 'SPACE') {
|
||||
chars.push('Space');
|
||||
} else {
|
||||
chars.push(v.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
return chars[0] || 'unknown';
|
||||
}
|
||||
101
app/common/keycodes.js
Normal file
101
app/common/keycodes.js
Normal file
@@ -0,0 +1,101 @@
|
||||
export default {
|
||||
backspace: 8,
|
||||
tab: 9,
|
||||
enter: 13,
|
||||
shift: 16,
|
||||
ctrl: 17,
|
||||
alt: 18,
|
||||
pausebreak: 19,
|
||||
capslock: 20,
|
||||
esc: 27,
|
||||
space: 32,
|
||||
pageup: 33,
|
||||
pagedown: 34,
|
||||
end: 35,
|
||||
home: 36,
|
||||
leftarrow: 37,
|
||||
uparrow: 38,
|
||||
rightarrow: 39,
|
||||
downarrow: 40,
|
||||
insert: 45,
|
||||
delete: 46,
|
||||
0: 48,
|
||||
1: 49,
|
||||
2: 50,
|
||||
3: 51,
|
||||
4: 52,
|
||||
5: 53,
|
||||
6: 54,
|
||||
7: 55,
|
||||
8: 56,
|
||||
9: 57,
|
||||
a: 65,
|
||||
b: 66,
|
||||
c: 67,
|
||||
d: 68,
|
||||
e: 69,
|
||||
f: 70,
|
||||
g: 71,
|
||||
h: 72,
|
||||
i: 73,
|
||||
j: 74,
|
||||
k: 75,
|
||||
l: 76,
|
||||
m: 77,
|
||||
n: 78,
|
||||
o: 79,
|
||||
p: 80,
|
||||
q: 81,
|
||||
r: 82,
|
||||
s: 83,
|
||||
t: 84,
|
||||
u: 85,
|
||||
v: 86,
|
||||
w: 87,
|
||||
x: 88,
|
||||
y: 89,
|
||||
z: 90,
|
||||
leftwindowkey: 91,
|
||||
rightwindowkey: 92,
|
||||
selectkey: 93,
|
||||
numpad0: 96,
|
||||
numpad1: 97,
|
||||
numpad2: 98,
|
||||
numpad3: 99,
|
||||
numpad4: 100,
|
||||
numpad5: 101,
|
||||
numpad6: 102,
|
||||
numpad7: 103,
|
||||
numpad8: 104,
|
||||
numpad9: 105,
|
||||
multiply: 106,
|
||||
add: 107,
|
||||
subtract: 109,
|
||||
decimalpoint: 110,
|
||||
divide: 111,
|
||||
f1: 112,
|
||||
f2: 113,
|
||||
f3: 114,
|
||||
f4: 115,
|
||||
f5: 116,
|
||||
f6: 117,
|
||||
f7: 118,
|
||||
f8: 119,
|
||||
f9: 120,
|
||||
f10: 121,
|
||||
f11: 122,
|
||||
f12: 123,
|
||||
numlock: 144,
|
||||
scrolllock: 145,
|
||||
semicolon: 186,
|
||||
equalsign: 187,
|
||||
comma: 188,
|
||||
dash: 189,
|
||||
period: 190,
|
||||
forwardslash: 191,
|
||||
graveaccent: 192,
|
||||
openbracket: 219,
|
||||
backslash: 220,
|
||||
closebracket: 221,
|
||||
singlequote: 222
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import urlMatchesCertHost from '../url-matches-cert-host';
|
||||
import {urlMatchesCertHost} from '../url-matches-cert-host';
|
||||
import {globalBeforeEach} from '../../__jest__/before-each';
|
||||
|
||||
describe('urlMatchesCertHost', () => {
|
||||
|
||||
@@ -25,7 +25,7 @@ import * as plugins from '../plugins/index';
|
||||
import * as pluginContexts from '../plugins/context/index';
|
||||
import {getAuthHeader} from './authentication';
|
||||
import {cookiesFromJar, jarFromCookies} from '../common/cookies';
|
||||
import urlMatchesCertHost from './url-matches-cert-host';
|
||||
import {urlMatchesCertHost} from './url-matches-cert-host';
|
||||
import aws4 from 'aws4';
|
||||
|
||||
export type ResponsePatch = {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {escapeRegex, setDefaultProtocol} from '../common/misc';
|
||||
|
||||
const DEFAULT_PORT = 443;
|
||||
|
||||
export default function urlMatchesCertHost (certificateHost, requestUrl) {
|
||||
export function urlMatchesCertHost (certificateHost, requestUrl) {
|
||||
const cHostWithProtocol = setDefaultProtocol(certificateHost, 'https:');
|
||||
const {hostname, port} = urlParse(requestUrl);
|
||||
const {hostname: cHostname, port: cPort} = certificateUrlParse(cHostWithProtocol);
|
||||
@@ -14,5 +14,9 @@ export default function urlMatchesCertHost (certificateHost, requestUrl) {
|
||||
|
||||
const cHostnameRegex = escapeRegex(cHostname || '').replace(/\\\*/g, '.*');
|
||||
|
||||
return (assumedCPort === assumedPort && !!hostname.match(`^${cHostnameRegex}$`));
|
||||
if (assumedCPort !== assumedPort) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!(hostname || '').match(`^${cHostnameRegex}$`);
|
||||
}
|
||||
|
||||
@@ -1,27 +1,21 @@
|
||||
// @flow
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import type {Hotkey as HotkeyType} from '../../../../common/hotkeys';
|
||||
import Hotkey from '../../hotkey';
|
||||
|
||||
type Props = {
|
||||
hotkey: HotkeyType,
|
||||
};
|
||||
|
||||
class DropdownHint extends PureComponent {
|
||||
props: Props;
|
||||
|
||||
render () {
|
||||
const {char, shift, alt} = this.props;
|
||||
const {hotkey} = this.props;
|
||||
return (
|
||||
<Hotkey
|
||||
className="dropdown__hint"
|
||||
char={char}
|
||||
alt={alt}
|
||||
shift={shift}
|
||||
/>
|
||||
<Hotkey className="dropdown__hint" hotkey={hotkey}/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DropdownHint.propTypes = {
|
||||
char: PropTypes.string.isRequired,
|
||||
|
||||
// Optional
|
||||
alt: PropTypes.bool,
|
||||
shift: PropTypes.bool
|
||||
};
|
||||
|
||||
export default DropdownHint;
|
||||
|
||||
@@ -7,6 +7,9 @@ import DropdownButton from './dropdown-button';
|
||||
import DropdownItem from './dropdown-item';
|
||||
import DropdownDivider from './dropdown-divider';
|
||||
import {fuzzyMatch} from '../../../../common/misc';
|
||||
import KeydownBinder from '../../keydown-binder';
|
||||
import * as hotkeys from '../../../../common/hotkeys';
|
||||
import {executeHotKey} from '../../../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class Dropdown extends PureComponent {
|
||||
@@ -113,10 +116,9 @@ class Dropdown extends PureComponent {
|
||||
|
||||
this._handleDropdownNavigation(e);
|
||||
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
executeHotKey(e, hotkeys.CLOSE_DROPDOWN, () => {
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_checkSizeAndPosition () {
|
||||
@@ -242,8 +244,6 @@ class Dropdown extends PureComponent {
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.body.addEventListener('keydown', this._handleBodyKeyDown);
|
||||
|
||||
// Move the element to the body so we can position absolutely
|
||||
if (this._dropdownMenu) {
|
||||
const el = ReactDOM.findDOMNode(this._dropdownMenu);
|
||||
@@ -252,8 +252,6 @@ class Dropdown extends PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.body.removeEventListener('keydown', this._handleBodyKeyDown);
|
||||
|
||||
// Remove the element from the body
|
||||
if (this._dropdownMenu) {
|
||||
const el = ReactDOM.findDOMNode(this._dropdownMenu);
|
||||
@@ -272,7 +270,7 @@ class Dropdown extends PureComponent {
|
||||
this.props.onHide && this.props.onHide();
|
||||
}
|
||||
|
||||
show () {
|
||||
show (filterVisible = false) {
|
||||
const bodyHeight = document.body.getBoundingClientRect().height;
|
||||
const dropdownTop = this._node.getBoundingClientRect().top;
|
||||
const dropUp = dropdownTop > bodyHeight - 200;
|
||||
@@ -280,7 +278,7 @@ class Dropdown extends PureComponent {
|
||||
this.setState({
|
||||
open: true,
|
||||
dropUp,
|
||||
filterVisible: false,
|
||||
filterVisible,
|
||||
filter: '',
|
||||
filterItems: null,
|
||||
filterActiveIndex: -1,
|
||||
@@ -290,11 +288,11 @@ class Dropdown extends PureComponent {
|
||||
this.props.onOpen && this.props.onOpen();
|
||||
}
|
||||
|
||||
toggle () {
|
||||
toggle (filterVisible = false) {
|
||||
if (this.state.open) {
|
||||
this.hide();
|
||||
} else {
|
||||
this.show();
|
||||
this.show(filterVisible);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,14 +403,16 @@ class Dropdown extends PureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style}
|
||||
className={classes}
|
||||
ref={this._setRef}
|
||||
onClick={this._handleClick}
|
||||
tabIndex="-1"
|
||||
onMouseDown={this._handleMouseDown}>
|
||||
{finalChildren}
|
||||
</div>
|
||||
<KeydownBinder stopMetaPropagation onKeydown={this._handleBodyKeyDown} disabled={!open}>
|
||||
<div style={style}
|
||||
className={classes}
|
||||
ref={this._setRef}
|
||||
onClick={this._handleClick}
|
||||
tabIndex="-1"
|
||||
onMouseDown={this._handleMouseDown}>
|
||||
{finalChildren}
|
||||
</div>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import autobind from 'autobind-decorator';
|
||||
import classnames from 'classnames';
|
||||
import {isMac} from '../../../common/constants';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
import {pressedHotKey} from '../../../common/hotkeys';
|
||||
|
||||
// Keep global z-index reference so that every modal will
|
||||
// appear over top of an existing one.
|
||||
@@ -25,20 +27,13 @@ class Modal extends PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't bubble up meta events up past the modal no matter what
|
||||
// Example: ctrl+Enter to send requests
|
||||
const isMeta = isMac() ? e.metaKey : e.ctrlKey;
|
||||
if (isMeta) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
// Don't check for close keys if we don't want them
|
||||
if (this.props.noEscape) {
|
||||
return;
|
||||
}
|
||||
|
||||
const closeOnKeyCodes = this.props.closeOnKeyCodes || [];
|
||||
const pressedEscape = e.keyCode === 27;
|
||||
const pressedEscape = pressedHotKey(e, hotkeys.CLOSE_MODAL);
|
||||
const pressedElse = closeOnKeyCodes.find(c => c === e.keyCode);
|
||||
|
||||
if (pressedEscape || pressedElse) {
|
||||
@@ -134,19 +129,20 @@ class Modal extends PureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={this._setModalRef}
|
||||
onKeyDown={this._handleKeyDown}
|
||||
tabIndex="-1"
|
||||
className={classes}
|
||||
style={styles}
|
||||
onClick={this._handleClick}>
|
||||
<div className="modal__backdrop overlay theme--overlay" data-close-modal></div>
|
||||
<div className="modal__content__wrapper">
|
||||
<div className="modal__content" key={forceRefreshCounter}>
|
||||
{children}
|
||||
<KeydownBinder stopMetaPropagation scoped onKeydown={this._handleKeyDown}>
|
||||
<div ref={this._setModalRef}
|
||||
tabIndex="-1"
|
||||
className={classes}
|
||||
style={styles}
|
||||
onClick={this._handleClick}>
|
||||
<div className="modal__backdrop overlay theme--overlay" data-close-modal></div>
|
||||
<div className="modal__content__wrapper">
|
||||
<div className="modal__content" key={forceRefreshCounter}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ import {Dropdown, DropdownButton, DropdownDivider, DropdownHint, DropdownItem} f
|
||||
import {showModal} from '../modals/index';
|
||||
import {trackEvent} from '../../../analytics/index';
|
||||
import Tooltip from '../tooltip';
|
||||
import {executeHotKey} from '../../../common/hotkeys';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
|
||||
@autobind
|
||||
class EnvironmentsDropdown extends PureComponent {
|
||||
@@ -18,6 +21,10 @@ class EnvironmentsDropdown extends PureComponent {
|
||||
showModal(EnvironmentsModal, this.props.workspace);
|
||||
}
|
||||
|
||||
_setDropdownRef (n) {
|
||||
this._dropdown = n;
|
||||
}
|
||||
|
||||
renderEnvironmentItem (environment) {
|
||||
return (
|
||||
<DropdownItem key={environment._id}
|
||||
@@ -29,6 +36,12 @@ class EnvironmentsDropdown extends PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
_handleKeydown (e) {
|
||||
executeHotKey(e, hotkeys.TOGGLE_ENVIRONMENTS_MENU, () => {
|
||||
this._dropdown && this._dropdown.toggle(true);
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
className,
|
||||
@@ -52,40 +65,42 @@ class EnvironmentsDropdown extends PureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<Dropdown {...other} className={className}>
|
||||
<DropdownButton className="btn btn--super-compact no-wrap">
|
||||
<div className="sidebar__menu__thing">
|
||||
{(!activeEnvironment && subEnvironments.length > 0) && (
|
||||
<Tooltip message="No environments active. Please select one to use."
|
||||
className="space-right"
|
||||
position="right">
|
||||
<i className="fa fa-exclamation-triangle notice"/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<div className="sidebar__menu__thing__text">
|
||||
{activeEnvironment && activeEnvironment.color && (
|
||||
<i className="fa fa-circle space-right" style={{color: activeEnvironment.color}}/>
|
||||
<KeydownBinder onKeydown={this._handleKeydown}>
|
||||
<Dropdown ref={this._setDropdownRef} {...other} className={className}>
|
||||
<DropdownButton className="btn btn--super-compact no-wrap">
|
||||
<div className="sidebar__menu__thing">
|
||||
{(!activeEnvironment && subEnvironments.length > 0) && (
|
||||
<Tooltip message="No environments active. Please select one to use."
|
||||
className="space-right"
|
||||
position="right">
|
||||
<i className="fa fa-exclamation-triangle notice"/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{description}
|
||||
<div className="sidebar__menu__thing__text">
|
||||
{activeEnvironment && activeEnvironment.color && (
|
||||
<i className="fa fa-circle space-right" style={{color: activeEnvironment.color}}/>
|
||||
)}
|
||||
{description}
|
||||
</div>
|
||||
<i className="space-left fa fa-caret-down"/>
|
||||
</div>
|
||||
<i className="space-left fa fa-caret-down"/>
|
||||
</div>
|
||||
</DropdownButton>
|
||||
</DropdownButton>
|
||||
|
||||
<DropdownDivider>Activate Environment</DropdownDivider>
|
||||
{subEnvironments.map(this.renderEnvironmentItem)}
|
||||
<DropdownDivider>Activate Environment</DropdownDivider>
|
||||
{subEnvironments.map(this.renderEnvironmentItem)}
|
||||
|
||||
<DropdownItem value={null} onClick={this._handleActivateEnvironment}>
|
||||
<i className="fa fa-empty"/> No Environment
|
||||
</DropdownItem>
|
||||
<DropdownItem value={null} onClick={this._handleActivateEnvironment}>
|
||||
<i className="fa fa-empty"/> No Environment
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownDivider>General</DropdownDivider>
|
||||
<DropdownDivider>General</DropdownDivider>
|
||||
|
||||
<DropdownItem onClick={this._handleShowEnvironmentModal}>
|
||||
<i className="fa fa-wrench"/> Manage Environments
|
||||
<DropdownHint char="E"/>
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
<DropdownItem onClick={this._handleShowEnvironmentModal}>
|
||||
<i className="fa fa-wrench"/> Manage Environments
|
||||
<DropdownHint hotkey={hotkeys.SHOW_ENVIRONMENTS}/>
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ const LOCALSTORAGE_KEY = 'insomnia.httpMethods';
|
||||
|
||||
@autobind
|
||||
class MethodDropdown extends PureComponent {
|
||||
_setDropdownRef (n) {
|
||||
this._dropdown = n;
|
||||
}
|
||||
|
||||
_handleSetCustomMethod () {
|
||||
let recentMethods;
|
||||
try {
|
||||
@@ -62,6 +66,10 @@ class MethodDropdown extends PureComponent {
|
||||
trackEvent('Request', 'Set Method', method);
|
||||
}
|
||||
|
||||
toggle () {
|
||||
this._dropdown && this._dropdown.toggle(true);
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
method,
|
||||
@@ -70,7 +78,7 @@ class MethodDropdown extends PureComponent {
|
||||
...extraProps
|
||||
} = this.props;
|
||||
return (
|
||||
<Dropdown className="method-dropdown" right={right}>
|
||||
<Dropdown ref={this._setDropdownRef} className="method-dropdown" right={right}>
|
||||
<DropdownButton type="button" {...extraProps}>
|
||||
{method} <i className="fa fa-caret-down"/>
|
||||
</DropdownButton>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {Dropdown, DropdownButton, DropdownHint, DropdownItem} from '../base/drop
|
||||
import * as models from '../../../models';
|
||||
import {trackEvent} from '../../../analytics/index';
|
||||
import {DropdownDivider} from '../base/dropdown/index';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class RequestActionsDropdown extends PureComponent {
|
||||
@@ -53,10 +54,11 @@ class RequestActionsDropdown extends PureComponent {
|
||||
</DropdownButton>
|
||||
<DropdownItem onClick={this._handleDuplicate}>
|
||||
<i className="fa fa-copy"/> Duplicate
|
||||
<DropdownHint char="D"/>
|
||||
<DropdownHint hotkey={hotkeys.DUPLICATE_REQUEST}/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleGenerateCode}>
|
||||
<i className="fa fa-code"/> Generate Code
|
||||
<DropdownHint hotkey={hotkeys.GENERATE_CODE}/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleCopyAsCurl}
|
||||
buttonClass={PromptButton}
|
||||
@@ -72,7 +74,7 @@ class RequestActionsDropdown extends PureComponent {
|
||||
|
||||
<DropdownItem onClick={handleShowSettings}>
|
||||
<i className="fa fa-wrench"/> Settings
|
||||
<DropdownHint alt shift char=","/>
|
||||
<DropdownHint hotkey={hotkeys.SHOW_REQUEST_SETTINGS}/>
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as models from '../../../models';
|
||||
import {showModal} from '../modals';
|
||||
import {trackEvent} from '../../../analytics/index';
|
||||
import {showPrompt} from '../modals/index';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class RequestGroupActionsDropdown extends PureComponent {
|
||||
@@ -69,10 +70,11 @@ class RequestGroupActionsDropdown extends PureComponent {
|
||||
</DropdownButton>
|
||||
<DropdownItem onClick={this._handleRequestCreate}>
|
||||
<i className="fa fa-plus-circle"/> New Request
|
||||
<DropdownHint char="N"></DropdownHint>
|
||||
<DropdownHint hotkey={hotkeys.CREATE_REQUEST}/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleRequestGroupCreate}>
|
||||
<i className="fa fa-folder"/> New Folder
|
||||
<DropdownHint hotkey={hotkeys.CREATE_FOLDER}/>
|
||||
</DropdownItem>
|
||||
<DropdownDivider />
|
||||
<DropdownItem onClick={this._handleRequestGroupDuplicate}>
|
||||
|
||||
@@ -7,9 +7,16 @@ import StatusTag from '../tags/status-tag';
|
||||
import TimeTag from '../tags/time-tag';
|
||||
import PromptButton from '../base/prompt-button';
|
||||
import {trackEvent} from '../../../analytics/index';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
import {executeHotKey} from '../../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class ResponseHistoryDropdown extends PureComponent {
|
||||
_setDropdownRef (n) {
|
||||
this._dropdown = n;
|
||||
}
|
||||
|
||||
_handleDeleteResponses () {
|
||||
trackEvent('History', 'Delete Responses');
|
||||
this.props.handleDeleteResponses(this.props.requestId);
|
||||
@@ -25,6 +32,12 @@ class ResponseHistoryDropdown extends PureComponent {
|
||||
this.props.handleSetActiveResponse(response);
|
||||
}
|
||||
|
||||
_handleKeydown (e) {
|
||||
executeHotKey(e, hotkeys.TOGGLE_HISTORY_DROPDOWN, () => {
|
||||
this._dropdown && this._dropdown.toggle(true);
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
clearTimeout(this._interval);
|
||||
}
|
||||
@@ -68,28 +81,31 @@ class ResponseHistoryDropdown extends PureComponent {
|
||||
const isLatestResponseActive = !responses.length || activeResponse._id === responses[0]._id;
|
||||
|
||||
return (
|
||||
<Dropdown key={activeResponse ? activeResponse._id : 'n/a'} {...extraProps}>
|
||||
<DropdownButton className="btn btn--super-compact tall">
|
||||
{isLatestResponseActive
|
||||
? <i className="fa fa-history"/>
|
||||
: <i className="fa fa-thumb-tack"/>}
|
||||
</DropdownButton>
|
||||
<DropdownDivider>Response History</DropdownDivider>
|
||||
<DropdownItem buttonClass={PromptButton}
|
||||
addIcon
|
||||
onClick={this._handleDeleteResponse}>
|
||||
<i className="fa fa-trash-o"/>
|
||||
Delete Current Response
|
||||
</DropdownItem>
|
||||
<DropdownItem buttonClass={PromptButton}
|
||||
addIcon
|
||||
onClick={this._handleDeleteResponses}>
|
||||
<i className="fa fa-trash-o"/>
|
||||
Clear History
|
||||
</DropdownItem>
|
||||
<DropdownDivider>Past Responses</DropdownDivider>
|
||||
{responses.map(this.renderDropdownItem)}
|
||||
</Dropdown>
|
||||
<KeydownBinder onKeydown={this._handleKeydown}>
|
||||
<Dropdown ref={this._setDropdownRef}
|
||||
key={activeResponse ? activeResponse._id : 'n/a'} {...extraProps}>
|
||||
<DropdownButton className="btn btn--super-compact tall">
|
||||
{isLatestResponseActive
|
||||
? <i className="fa fa-history"/>
|
||||
: <i className="fa fa-thumb-tack"/>}
|
||||
</DropdownButton>
|
||||
<DropdownDivider>Response History</DropdownDivider>
|
||||
<DropdownItem buttonClass={PromptButton}
|
||||
addIcon
|
||||
onClick={this._handleDeleteResponse}>
|
||||
<i className="fa fa-trash-o"/>
|
||||
Delete Current Response
|
||||
</DropdownItem>
|
||||
<DropdownItem buttonClass={PromptButton}
|
||||
addIcon
|
||||
onClick={this._handleDeleteResponses}>
|
||||
<i className="fa fa-trash-o"/>
|
||||
Clear History
|
||||
</DropdownItem>
|
||||
<DropdownDivider>Past Responses</DropdownDivider>
|
||||
{responses.map(this.renderDropdownItem)}
|
||||
</Dropdown>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ import WorkspaceShareSettingsModal from '../modals/workspace-share-settings-moda
|
||||
import * as session from '../../../sync/session';
|
||||
import LoginModal from '../modals/login-modal';
|
||||
import Tooltip from '../tooltip';
|
||||
import {executeHotKey} from '../../../common/hotkeys';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
|
||||
@autobind
|
||||
class WorkspaceDropdown extends PureComponent {
|
||||
@@ -44,6 +47,10 @@ class WorkspaceDropdown extends PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
_setDropdownRef (n) {
|
||||
this._dropdown = n;
|
||||
}
|
||||
|
||||
_handleShowLogin () {
|
||||
showModal(LoginModal);
|
||||
}
|
||||
@@ -90,6 +97,12 @@ class WorkspaceDropdown extends PureComponent {
|
||||
});
|
||||
}
|
||||
|
||||
_handleKeydown (e) {
|
||||
executeHotKey(e, hotkeys.TOGGLE_MAIN_MENU, () => {
|
||||
this._dropdown && this._dropdown.toggle(true);
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
className,
|
||||
@@ -112,83 +125,86 @@ class WorkspaceDropdown extends PureComponent {
|
||||
);
|
||||
|
||||
return (
|
||||
<Dropdown beside
|
||||
className={classes}
|
||||
onOpen={this._handleDropdownOpen}
|
||||
onHide={this._handleDropdownHide}
|
||||
{...other}>
|
||||
<DropdownButton className="btn wide">
|
||||
<h1 className="no-pad text-left">
|
||||
<div className="pull-right">
|
||||
{isLoading ? <i className="fa fa-refresh fa-spin"/> : null}
|
||||
{unseenWorkspaces.length > 0 && (
|
||||
<Tooltip message={unseenWorkspacesMessage} position="bottom">
|
||||
<i className="fa fa-asterisk space-left"/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<i className="fa fa-caret-down space-left"/>
|
||||
</div>
|
||||
{activeWorkspace.name}
|
||||
</h1>
|
||||
</DropdownButton>
|
||||
<DropdownDivider>{activeWorkspace.name}</DropdownDivider>
|
||||
<DropdownItem onClick={this._handleShowWorkspaceSettings}>
|
||||
<i className="fa fa-wrench"/> Workspace Settings
|
||||
<DropdownHint shift char=","/>
|
||||
</DropdownItem>
|
||||
<KeydownBinder onKeydown={this._handleKeydown}>
|
||||
<Dropdown beside
|
||||
ref={this._setDropdownRef}
|
||||
className={classes}
|
||||
onOpen={this._handleDropdownOpen}
|
||||
onHide={this._handleDropdownHide}
|
||||
{...other}>
|
||||
<DropdownButton className="btn wide">
|
||||
<h1 className="no-pad text-left">
|
||||
<div className="pull-right">
|
||||
{isLoading ? <i className="fa fa-refresh fa-spin"/> : null}
|
||||
{unseenWorkspaces.length > 0 && (
|
||||
<Tooltip message={unseenWorkspacesMessage} position="bottom">
|
||||
<i className="fa fa-asterisk space-left"/>
|
||||
</Tooltip>
|
||||
)}
|
||||
<i className="fa fa-caret-down space-left"/>
|
||||
</div>
|
||||
{activeWorkspace.name}
|
||||
</h1>
|
||||
</DropdownButton>
|
||||
<DropdownDivider>{activeWorkspace.name}</DropdownDivider>
|
||||
<DropdownItem onClick={this._handleShowWorkspaceSettings}>
|
||||
<i className="fa fa-wrench"/> Workspace Settings
|
||||
<DropdownHint hotkey={hotkeys.SHOW_WORKSPACE_SETTINGS}/>
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownItem onClick={this._handleShowShareSettings}>
|
||||
<i className="fa fa-globe"/> Share <strong>{activeWorkspace.name}</strong>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleShowShareSettings}>
|
||||
<i className="fa fa-globe"/> Share <strong>{activeWorkspace.name}</strong>
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownDivider>Switch Workspace</DropdownDivider>
|
||||
<DropdownDivider>Switch Workspace</DropdownDivider>
|
||||
|
||||
{nonActiveWorkspaces.map(w => {
|
||||
const isUnseen = !!unseenWorkspaces.find(v => v._id === w._id);
|
||||
return (
|
||||
<DropdownItem key={w._id} onClick={this._handleSwitchWorkspace} value={w._id}>
|
||||
<i className="fa fa-random"/> To <strong>{w.name}</strong>
|
||||
{isUnseen && (
|
||||
<Tooltip message="This workspace is new">
|
||||
<i className="width-auto fa fa-asterisk surprise"/>
|
||||
</Tooltip>
|
||||
)}
|
||||
{nonActiveWorkspaces.map(w => {
|
||||
const isUnseen = !!unseenWorkspaces.find(v => v._id === w._id);
|
||||
return (
|
||||
<DropdownItem key={w._id} onClick={this._handleSwitchWorkspace} value={w._id}>
|
||||
<i className="fa fa-random"/> To <strong>{w.name}</strong>
|
||||
{isUnseen && (
|
||||
<Tooltip message="This workspace is new">
|
||||
<i className="width-auto fa fa-asterisk surprise"/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</DropdownItem>
|
||||
);
|
||||
})}
|
||||
|
||||
<DropdownItem onClick={this._handleWorkspaceCreate}>
|
||||
<i className="fa fa-empty"/> New Workspace
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownDivider>Insomnia Version {getAppVersion()}</DropdownDivider>
|
||||
|
||||
<DropdownItem onClick={this._handleShowSettings}>
|
||||
<i className="fa fa-cog"/> Preferences
|
||||
<DropdownHint hotkey={hotkeys.SHOW_SETTINGS}/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleShowExport}>
|
||||
<i className="fa fa-share"/> Import/Export
|
||||
</DropdownItem>
|
||||
|
||||
{/* Not Logged In */}
|
||||
|
||||
{!this.state.loggedIn && (
|
||||
<DropdownItem key="login" onClick={this._handleShowLogin}>
|
||||
<i className="fa fa-sign-in"/> Log In
|
||||
</DropdownItem>
|
||||
);
|
||||
})}
|
||||
)}
|
||||
|
||||
<DropdownItem onClick={this._handleWorkspaceCreate}>
|
||||
<i className="fa fa-empty"/> New Workspace
|
||||
</DropdownItem>
|
||||
|
||||
<DropdownDivider>Insomnia Version {getAppVersion()}</DropdownDivider>
|
||||
|
||||
<DropdownItem onClick={this._handleShowSettings}>
|
||||
<i className="fa fa-cog"/> Preferences
|
||||
<DropdownHint char=","/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleShowExport}>
|
||||
<i className="fa fa-share"/> Import/Export
|
||||
</DropdownItem>
|
||||
|
||||
{/* Not Logged In */}
|
||||
|
||||
{!this.state.loggedIn && (
|
||||
<DropdownItem key="login" onClick={this._handleShowLogin}>
|
||||
<i className="fa fa-sign-in"/> Log In
|
||||
</DropdownItem>
|
||||
)}
|
||||
|
||||
{!this.state.loggedIn && (
|
||||
<DropdownItem key="invite"
|
||||
buttonClass={Link}
|
||||
href="https://insomnia.rest/pricing/"
|
||||
button>
|
||||
<i className="fa fa-users"/> Upgrade to Plus
|
||||
<i className="fa fa-star surprise fa-outline"/>
|
||||
</DropdownItem>
|
||||
)}
|
||||
</Dropdown>
|
||||
{!this.state.loggedIn && (
|
||||
<DropdownItem key="invite"
|
||||
buttonClass={Link}
|
||||
href="https://insomnia.rest/pricing/"
|
||||
button>
|
||||
<i className="fa fa-users"/> Upgrade to Plus
|
||||
<i className="fa fa-star surprise fa-outline"/>
|
||||
</DropdownItem>
|
||||
)}
|
||||
</Dropdown>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,8 @@ class OAuth2Auth extends React.PureComponent {
|
||||
'redirectUrl',
|
||||
this._handleChangeRedirectUrl,
|
||||
'This can be whatever you want or need it to be. Insomnia will automatically ' +
|
||||
'detect the redirect from the browser window and extract the credentials.'
|
||||
'detect a redirect in the client browser window and extract the code from the ' +
|
||||
'redirected URL'
|
||||
);
|
||||
|
||||
const state = this.renderInputRow(
|
||||
|
||||
@@ -1,34 +1,40 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import type {Hotkey as HotkeyType} from '../../common/hotkeys';
|
||||
import {ALT_SYM, CTRL_SYM, isMac, joinHotKeys, MOD_SYM, SHIFT_SYM} from '../../common/constants';
|
||||
import * as hotkeys from '../../common/hotkeys';
|
||||
|
||||
type Props = {
|
||||
hotkey: HotkeyType,
|
||||
|
||||
// Optional
|
||||
className?: string
|
||||
};
|
||||
|
||||
class Hotkey extends React.PureComponent {
|
||||
props: Props;
|
||||
|
||||
class Hotkey extends PureComponent {
|
||||
render () {
|
||||
const {char, shift, alt, ctrl, className} = this.props;
|
||||
const {hotkey, className} = this.props;
|
||||
const {alt, shift, meta} = hotkey;
|
||||
const chars = [ ];
|
||||
|
||||
alt && chars.push(ALT_SYM);
|
||||
shift && chars.push(SHIFT_SYM);
|
||||
ctrl && chars.push(CTRL_SYM);
|
||||
!ctrl && chars.push(MOD_SYM);
|
||||
chars.push(char);
|
||||
|
||||
if (meta) {
|
||||
chars.push(isMac() ? MOD_SYM : CTRL_SYM);
|
||||
}
|
||||
|
||||
chars.push(hotkeys.getChar(hotkey));
|
||||
|
||||
return (
|
||||
<span className={`${isMac() ? 'font-normal' : ''} ${className}`}>
|
||||
<span className={classnames(className, {'font-normal': isMac()})}>
|
||||
{joinHotKeys(chars)}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Hotkey.propTypes = {
|
||||
char: PropTypes.string.isRequired,
|
||||
|
||||
// Optional
|
||||
alt: PropTypes.bool,
|
||||
shift: PropTypes.bool,
|
||||
ctrl: PropTypes.bool,
|
||||
className: PropTypes.string
|
||||
};
|
||||
|
||||
export default Hotkey;
|
||||
|
||||
57
app/ui/components/keydown-binder.js
Normal file
57
app/ui/components/keydown-binder.js
Normal file
@@ -0,0 +1,57 @@
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import autobind from 'autobind-decorator';
|
||||
import {isMac} from '../../common/constants';
|
||||
|
||||
type Props = {
|
||||
onKeydown: Function,
|
||||
children: React.Children,
|
||||
disabled?: boolean,
|
||||
scoped?: boolean,
|
||||
stopMetaPropagation?: boolean
|
||||
};
|
||||
|
||||
@autobind
|
||||
class KeydownBinder extends React.Component {
|
||||
props: Props;
|
||||
|
||||
_handleKeydown (e: KeyboardEvent) {
|
||||
const {stopMetaPropagation, onKeydown, disabled} = this.props;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMeta = isMac() ? e.metaKey : e.ctrlKey;
|
||||
if (stopMetaPropagation && isMeta) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
onKeydown(e);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
if (this.props.scoped) {
|
||||
const el = ReactDOM.findDOMNode(this);
|
||||
el && el.addEventListener('keydown', this._handleKeydown);
|
||||
} else {
|
||||
document.body && document.body.addEventListener('keydown', this._handleKeydown);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.props.scoped) {
|
||||
const el = ReactDOM.findDOMNode(this);
|
||||
el && el.removeEventListener('keydown', this._handleKeydown);
|
||||
} else {
|
||||
document.body && document.body.removeEventListener('keydown', this._handleKeydown);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default KeydownBinder;
|
||||
@@ -103,8 +103,10 @@ class WorkspaceShareSettingsModal extends PureComponent {
|
||||
|
||||
async show () {
|
||||
this._resetState();
|
||||
await this._load();
|
||||
this.modal.show();
|
||||
|
||||
// This takes a while, so do it after show()
|
||||
await this._load();
|
||||
}
|
||||
|
||||
hide () {
|
||||
|
||||
@@ -24,6 +24,7 @@ import {showModal} from './modals/index';
|
||||
import RequestSettingsModal from './modals/request-settings-modal';
|
||||
import MarkdownPreview from './markdown-preview';
|
||||
import type {Settings} from '../../models/settings';
|
||||
import * as hotkeys from '../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class RequestPane extends React.PureComponent {
|
||||
@@ -199,19 +200,19 @@ class RequestPane extends React.PureComponent {
|
||||
<tr>
|
||||
<td>New Request</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="N"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.CREATE_REQUEST}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Switch Requests</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="P"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.SHOW_QUICK_SWITCHER}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edit Environments</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="E"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.SHOW_ENVIRONMENTS}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -9,6 +9,9 @@ import {showPrompt} from './modals/index';
|
||||
import MethodDropdown from './dropdowns/method-dropdown';
|
||||
import PromptButton from './base/prompt-button';
|
||||
import OneLineEditor from './codemirror/one-line-editor';
|
||||
import {executeHotKey} from '../../common/hotkeys';
|
||||
import * as hotkeys from '../../common/hotkeys';
|
||||
import KeydownBinder from './keydown-binder';
|
||||
|
||||
@autobind
|
||||
class RequestUrlBar extends PureComponent {
|
||||
@@ -28,6 +31,10 @@ class RequestUrlBar extends PureComponent {
|
||||
this._dropdown = n;
|
||||
}
|
||||
|
||||
_setMethodDropdownRef (n) {
|
||||
this._methodDropdown = n;
|
||||
}
|
||||
|
||||
_setInputRef (n) {
|
||||
this._input = n;
|
||||
}
|
||||
@@ -109,13 +116,25 @@ class RequestUrlBar extends PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
// meta+l
|
||||
const metaPressed = isMac() ? e.metaKey : e.ctrlKey;
|
||||
if (metaPressed && e.keyCode === 76) {
|
||||
e.preventDefault();
|
||||
executeHotKey(e, hotkeys.FOCUS_URL, () => {
|
||||
if (!this._input) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._input.focus();
|
||||
this._input.selectAll();
|
||||
}
|
||||
});
|
||||
|
||||
executeHotKey(e, hotkeys.TOGGLE_METHOD_DROPDOWN, () => {
|
||||
if (!this._methodDropdown) {
|
||||
return;
|
||||
}
|
||||
this._methodDropdown.toggle();
|
||||
});
|
||||
|
||||
executeHotKey(e, hotkeys.SHOW_SEND_OPTIONS, () => {
|
||||
this._dropdown.toggle(true);
|
||||
});
|
||||
}
|
||||
|
||||
_handleSend () {
|
||||
@@ -201,14 +220,6 @@ class RequestUrlBar extends PureComponent {
|
||||
this._handleFormSubmit(e);
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
document.body.addEventListener('keydown', this._handleKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.body.removeEventListener('keydown', this._handleKeyDown);
|
||||
}
|
||||
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.requestId !== this.props.requestId) {
|
||||
this._handleResetTimeouts();
|
||||
@@ -252,7 +263,7 @@ class RequestUrlBar extends PureComponent {
|
||||
<DropdownDivider>Basic</DropdownDivider>
|
||||
<DropdownItem type="submit">
|
||||
<i className="fa fa-arrow-circle-o-right"/> Send Now
|
||||
<DropdownHint char="Enter"/>
|
||||
<DropdownHint hotkey={hotkeys.SEND_REQUEST}/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleGenerateCode}>
|
||||
<i className="fa fa-code"/> Generate Client Code
|
||||
@@ -296,26 +307,30 @@ class RequestUrlBar extends PureComponent {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className="urlbar">
|
||||
<MethodDropdown onChange={this._handleMethodChange} method={method}>
|
||||
{method} <i className="fa fa-caret-down"/>
|
||||
</MethodDropdown>
|
||||
<form onSubmit={this._handleFormSubmit}>
|
||||
<OneLineEditor
|
||||
key={uniquenessKey}
|
||||
ref={this._setInputRef}
|
||||
onPaste={this._handleUrlPaste}
|
||||
forceEditor
|
||||
type="text"
|
||||
render={handleRender}
|
||||
getAutocompleteConstants={handleAutocompleteUrls}
|
||||
getRenderContext={handleGetRenderContext}
|
||||
placeholder="https://api.myproduct.com/v1/users"
|
||||
defaultValue={url}
|
||||
onChange={this._handleUrlChange}/>
|
||||
{this.renderSendButton()}
|
||||
</form>
|
||||
</div>
|
||||
<KeydownBinder onKeydown={this._handleKeyDown}>
|
||||
<div className="urlbar">
|
||||
<MethodDropdown ref={this._setMethodDropdownRef}
|
||||
onChange={this._handleMethodChange}
|
||||
method={method}>
|
||||
{method} <i className="fa fa-caret-down"/>
|
||||
</MethodDropdown>
|
||||
<form onSubmit={this._handleFormSubmit}>
|
||||
<OneLineEditor
|
||||
key={uniquenessKey}
|
||||
ref={this._setInputRef}
|
||||
onPaste={this._handleUrlPaste}
|
||||
forceEditor
|
||||
type="text"
|
||||
render={handleRender}
|
||||
getAutocompleteConstants={handleAutocompleteUrls}
|
||||
getRenderContext={handleGetRenderContext}
|
||||
placeholder="https://api.myproduct.com/v1/users"
|
||||
defaultValue={url}
|
||||
onChange={this._handleUrlChange}/>
|
||||
{this.renderSendButton()}
|
||||
</form>
|
||||
</div>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import {getSetCookieHeaders, nullFn} from '../../common/misc';
|
||||
import {cancelCurrentRequest} from '../../network/network';
|
||||
import {trackEvent} from '../../analytics';
|
||||
import Hotkey from './hotkey';
|
||||
import * as hotkeys from '../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class ResponsePane extends PureComponent {
|
||||
@@ -186,25 +187,25 @@ class ResponsePane extends PureComponent {
|
||||
<tr>
|
||||
<td>Send Request</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="Enter"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.SEND_REQUEST}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Focus Url Bar</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="L"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.FOCUS_URL}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Manage Cookies</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="K"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.SHOW_COOKIES}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Edit Environments</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char="E"/></code>
|
||||
<code><Hotkey hotkey={hotkeys.SHOW_ENVIRONMENTS}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import autobind from 'autobind-decorator';
|
||||
import Hotkey from '../hotkey';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class Shortcuts extends PureComponent {
|
||||
renderHotkey (name, char, shift, alt, ctrl) {
|
||||
renderHotkey (hotkey, i) {
|
||||
return (
|
||||
<tr>
|
||||
<td>{name}</td>
|
||||
<tr key={i}>
|
||||
<td>{hotkey.description}</td>
|
||||
<td className="text-right">
|
||||
<code><Hotkey char={char} shift={shift} alt={alt} ctrl={ctrl}/></code>
|
||||
<code><Hotkey hotkey={hotkey}/></code>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
@@ -18,19 +21,24 @@ class Shortcuts extends PureComponent {
|
||||
<div>
|
||||
<table className="table--fancy">
|
||||
<tbody>
|
||||
{this.renderHotkey('Switch Requests', 'P')}
|
||||
{this.renderHotkey('Send Request', 'Enter')}
|
||||
{this.renderHotkey('New Request', 'N')}
|
||||
{this.renderHotkey('Duplicate Request', 'D')}
|
||||
{this.renderHotkey('Show Cookie Manager', 'K')}
|
||||
{this.renderHotkey('Show Environment Editor', 'E')}
|
||||
{this.renderHotkey('Focus URL Bar', 'L')}
|
||||
{this.renderHotkey('Toggle Sidebar', '\\')}
|
||||
{this.renderHotkey('Show Autocomplete Dropdown', 'Space', false, false, true)}
|
||||
{this.renderHotkey('Show App Preferences', ',')}
|
||||
{this.renderHotkey('Show Workspace Settings', ',', true)}
|
||||
{this.renderHotkey('Show Request Settings', ',', true, true)}
|
||||
{this.renderHotkey('Show Keyboard Shortcuts', '?')}
|
||||
{this.renderHotkey(hotkeys.SHOW_QUICK_SWITCHER)}
|
||||
{this.renderHotkey(hotkeys.SEND_REQUEST)}
|
||||
{this.renderHotkey(hotkeys.SHOW_SEND_OPTIONS)}
|
||||
{this.renderHotkey(hotkeys.CREATE_REQUEST)}
|
||||
{this.renderHotkey(hotkeys.CREATE_FOLDER)}
|
||||
{this.renderHotkey(hotkeys.DUPLICATE_REQUEST)}
|
||||
{this.renderHotkey(hotkeys.SHOW_COOKIES)}
|
||||
{this.renderHotkey(hotkeys.SHOW_ENVIRONMENTS)}
|
||||
{this.renderHotkey(hotkeys.TOGGLE_ENVIRONMENTS_MENU)}
|
||||
{this.renderHotkey(hotkeys.FOCUS_URL)}
|
||||
{this.renderHotkey(hotkeys.TOGGLE_METHOD_DROPDOWN)}
|
||||
{this.renderHotkey(hotkeys.TOGGLE_SIDEBAR)}
|
||||
{this.renderHotkey(hotkeys.TOGGLE_HISTORY_DROPDOWN)}
|
||||
{this.renderHotkey(hotkeys.SHOW_AUTOCOMPLETE)}
|
||||
{this.renderHotkey(hotkeys.SHOW_SETTINGS)}
|
||||
{this.renderHotkey(hotkeys.SHOW_WORKSPACE_SETTINGS)}
|
||||
{this.renderHotkey(hotkeys.SHOW_REQUEST_SETTINGS)}
|
||||
{this.renderHotkey(hotkeys.TOGGLE_MAIN_MENU)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -4,9 +4,16 @@ import autobind from 'autobind-decorator';
|
||||
import {Dropdown, DropdownHint, DropdownButton, DropdownItem} from '../base/dropdown';
|
||||
import {DEBOUNCE_MILLIS} from '../../../common/constants';
|
||||
import {trackEvent} from '../../../analytics/index';
|
||||
import KeydownBinder from '../keydown-binder';
|
||||
import {executeHotKey} from '../../../common/hotkeys';
|
||||
import * as hotkeys from '../../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class SidebarFilter extends PureComponent {
|
||||
_setInputRef (n) {
|
||||
this._input = n;
|
||||
}
|
||||
|
||||
_handleOnChange (e) {
|
||||
const value = e.target.value;
|
||||
|
||||
@@ -32,31 +39,40 @@ class SidebarFilter extends PureComponent {
|
||||
trackEvent('Request', 'Create', 'Sidebar Filter');
|
||||
}
|
||||
|
||||
_handleKeydown (e) {
|
||||
executeHotKey(e, hotkeys.FOCUS_FILTER, () => {
|
||||
this._input && this._input.focus();
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="sidebar__filter">
|
||||
<div className="form-control form-control--outlined">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter"
|
||||
defaultValue={this.props.filter}
|
||||
onChange={this._handleOnChange}
|
||||
/>
|
||||
<KeydownBinder onKeydown={this._handleKeydown}>
|
||||
<div className="sidebar__filter">
|
||||
<div className="form-control form-control--outlined">
|
||||
<input
|
||||
ref={this._setInputRef}
|
||||
type="text"
|
||||
placeholder="Filter"
|
||||
defaultValue={this.props.filter}
|
||||
onChange={this._handleOnChange}
|
||||
/>
|
||||
</div>
|
||||
<Dropdown right>
|
||||
<DropdownButton className="btn btn--compact">
|
||||
<i className="fa fa-plus-circle"/>
|
||||
</DropdownButton>
|
||||
<DropdownItem onClick={this._handleRequestCreate}>
|
||||
<i className="fa fa-plus-circle"/> New Request
|
||||
<DropdownHint hotkey={hotkeys.CREATE_REQUEST}/>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleRequestGroupCreate}>
|
||||
<i className="fa fa-folder"/> New Folder
|
||||
<DropdownHint hotkey={hotkeys.CREATE_FOLDER}/>
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
<Dropdown right>
|
||||
<DropdownButton className="btn btn--compact">
|
||||
<i className="fa fa-plus-circle"/>
|
||||
</DropdownButton>
|
||||
<DropdownItem onClick={this._handleRequestCreate}>
|
||||
<i className="fa fa-plus-circle"/> New Request
|
||||
<DropdownHint char="N"></DropdownHint>
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleRequestGroupCreate}>
|
||||
<i className="fa fa-folder"/> New Folder
|
||||
</DropdownItem>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ class SidebarRequestRow extends PureComponent {
|
||||
if (!request) {
|
||||
node = (
|
||||
<li className={classes}>
|
||||
<div className="sidebar__item" tabIndex={0}>
|
||||
<div className="sidebar__item">
|
||||
<button className="sidebar__clickable" onClick={this._handleRequestCreateFromEmpty}>
|
||||
<em className="faded">click to add first request...</em>
|
||||
</button>
|
||||
|
||||
@@ -16,7 +16,7 @@ import CookiesModal from '../components/modals/cookies-modal';
|
||||
import RequestSwitcherModal from '../components/modals/request-switcher-modal';
|
||||
import ChangelogModal from '../components/modals/changelog-modal';
|
||||
import SettingsModal, {TAB_INDEX_SHORTCUTS} from '../components/modals/settings-modal';
|
||||
import {COLLAPSE_SIDEBAR_REMS, DEFAULT_PANE_HEIGHT, DEFAULT_PANE_WIDTH, DEFAULT_SIDEBAR_WIDTH, getAppVersion, isMac, MAX_PANE_HEIGHT, MAX_PANE_WIDTH, MAX_SIDEBAR_REMS, MIN_PANE_HEIGHT, MIN_PANE_WIDTH, MIN_SIDEBAR_REMS, PREVIEW_MODE_SOURCE} from '../../common/constants';
|
||||
import {COLLAPSE_SIDEBAR_REMS, DEFAULT_PANE_HEIGHT, DEFAULT_PANE_WIDTH, DEFAULT_SIDEBAR_WIDTH, getAppVersion, MAX_PANE_HEIGHT, MAX_PANE_WIDTH, MAX_SIDEBAR_REMS, MIN_PANE_HEIGHT, MIN_PANE_WIDTH, MIN_SIDEBAR_REMS, PREVIEW_MODE_SOURCE} from '../../common/constants';
|
||||
import * as globalActions from '../redux/modules/global';
|
||||
import * as db from '../../common/database';
|
||||
import * as models from '../../models';
|
||||
@@ -35,17 +35,9 @@ import * as render from '../../common/render';
|
||||
import {getKeys} from '../../templating/utils';
|
||||
import {showAlert, showPrompt} from '../components/modals/index';
|
||||
import {exportHar} from '../../common/har';
|
||||
|
||||
const KEY_ENTER = 13;
|
||||
const KEY_COMMA = 188;
|
||||
const KEY_D = 68;
|
||||
const KEY_E = 69;
|
||||
const KEY_K = 75;
|
||||
const KEY_L = 76;
|
||||
const KEY_N = 78;
|
||||
const KEY_P = 80;
|
||||
const KEY_R = 82;
|
||||
const KEY_F5 = 116;
|
||||
import * as hotkeys from '../../common/hotkeys';
|
||||
import KeydownBinder from '../components/keydown-binder';
|
||||
import {executeHotKey} from '../../common/hotkeys';
|
||||
|
||||
@autobind
|
||||
class App extends PureComponent {
|
||||
@@ -66,106 +58,52 @@ class App extends PureComponent {
|
||||
|
||||
this._savePaneWidth = debounce(paneWidth => this._updateActiveWorkspaceMeta({paneWidth}));
|
||||
this._savePaneHeight = debounce(paneHeight => this._updateActiveWorkspaceMeta({paneHeight}));
|
||||
this._saveSidebarWidth = debounce(sidebarWidth => this._updateActiveWorkspaceMeta({sidebarWidth}));
|
||||
this._saveSidebarWidth = debounce(
|
||||
sidebarWidth => this._updateActiveWorkspaceMeta({sidebarWidth}));
|
||||
|
||||
this._globalKeyMap = null;
|
||||
}
|
||||
|
||||
_setGlobalKeyMap () {
|
||||
this._globalKeyMap = [
|
||||
{ // Show Workspace Settings
|
||||
meta: true,
|
||||
shift: true,
|
||||
alt: false,
|
||||
key: KEY_COMMA,
|
||||
callback: () => {
|
||||
const {activeWorkspace} = this.props;
|
||||
showModal(WorkspaceSettingsModal, activeWorkspace);
|
||||
trackEvent('HotKey', 'Workspace Settings');
|
||||
[hotkeys.SHOW_WORKSPACE_SETTINGS, () => {
|
||||
const {activeWorkspace} = this.props;
|
||||
showModal(WorkspaceSettingsModal, activeWorkspace);
|
||||
}],
|
||||
[hotkeys.SHOW_REQUEST_SETTINGS, () => {
|
||||
if (this.props.activeRequest) {
|
||||
showModal(RequestSettingsModal, {request: this.props.activeRequest});
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: true,
|
||||
alt: true,
|
||||
key: KEY_COMMA,
|
||||
callback: () => {
|
||||
if (this.props.activeRequest) {
|
||||
showModal(RequestSettingsModal, {request: this.props.activeRequest});
|
||||
trackEvent('HotKey', 'Request Settings');
|
||||
}
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_P,
|
||||
callback: () => {
|
||||
showModal(RequestSwitcherModal);
|
||||
trackEvent('HotKey', 'Quick Switcher');
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: [KEY_ENTER, KEY_R],
|
||||
callback: this._handleSendShortcut
|
||||
}, {
|
||||
meta: false,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: [KEY_F5],
|
||||
callback: this._handleSendShortcut
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_E,
|
||||
callback: () => {
|
||||
const {activeWorkspace} = this.props;
|
||||
showModal(WorkspaceEnvironmentsEditModal, activeWorkspace);
|
||||
trackEvent('HotKey', 'Environments');
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_L,
|
||||
callback: () => {
|
||||
const node = document.body.querySelector('.urlbar input');
|
||||
node && node.focus();
|
||||
trackEvent('HotKey', 'Url');
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_K,
|
||||
callback: () => {
|
||||
const {activeWorkspace} = this.props;
|
||||
showModal(CookiesModal, activeWorkspace);
|
||||
trackEvent('HotKey', 'Cookies');
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_N,
|
||||
callback: () => {
|
||||
const {activeRequest, activeWorkspace} = this.props;
|
||||
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
||||
this._requestCreate(parentId);
|
||||
trackEvent('HotKey', 'Request Create');
|
||||
}
|
||||
}, {
|
||||
meta: true,
|
||||
shift: false,
|
||||
alt: false,
|
||||
key: KEY_D,
|
||||
callback: async () => {
|
||||
await this._requestDuplicate(this.props.activeRequest);
|
||||
trackEvent('HotKey', 'Request Duplicate');
|
||||
}
|
||||
}
|
||||
}],
|
||||
[hotkeys.SHOW_QUICK_SWITCHER, () => {
|
||||
showModal(RequestSwitcherModal);
|
||||
}],
|
||||
[hotkeys.SEND_REQUEST, this._handleSendShortcut],
|
||||
[hotkeys.SEND_REQUEST_F5, this._handleSendShortcut],
|
||||
[hotkeys.SHOW_ENVIRONMENTS, () => {
|
||||
const {activeWorkspace} = this.props;
|
||||
showModal(WorkspaceEnvironmentsEditModal, activeWorkspace);
|
||||
}],
|
||||
[hotkeys.SHOW_COOKIES, () => {
|
||||
const {activeWorkspace} = this.props;
|
||||
showModal(CookiesModal, activeWorkspace);
|
||||
}],
|
||||
[hotkeys.CREATE_REQUEST, () => {
|
||||
const {activeRequest, activeWorkspace} = this.props;
|
||||
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
||||
this._requestCreate(parentId);
|
||||
}],
|
||||
[hotkeys.CREATE_FOLDER, () => {
|
||||
const {activeRequest, activeWorkspace} = this.props;
|
||||
const parentId = activeRequest ? activeRequest.parentId : activeWorkspace._id;
|
||||
this._requestGroupCreate(parentId);
|
||||
}],
|
||||
[hotkeys.GENERATE_CODE, async () => {
|
||||
showModal(GenerateCodeModal, this.props.activeRequest);
|
||||
}],
|
||||
[hotkeys.DUPLICATE_REQUEST, async () => {
|
||||
await this._requestDuplicate(this.props.activeRequest);
|
||||
}]
|
||||
];
|
||||
}
|
||||
|
||||
@@ -175,7 +113,6 @@ class App extends PureComponent {
|
||||
activeRequest ? activeRequest._id : 'n/a',
|
||||
activeEnvironment ? activeEnvironment._id : 'n/a',
|
||||
);
|
||||
trackEvent('HotKey', 'Send');
|
||||
}
|
||||
|
||||
_setRequestPaneRef (n) {
|
||||
@@ -629,31 +566,8 @@ class App extends PureComponent {
|
||||
}
|
||||
|
||||
_handleKeyDown (e) {
|
||||
const isMetaPressed = isMac() ? e.metaKey : e.ctrlKey;
|
||||
const isAltPressed = isMac() ? e.ctrlKey : e.altKey;
|
||||
const isShiftPressed = e.shiftKey;
|
||||
|
||||
for (const {meta, shift, alt, key, callback} of this._globalKeyMap) {
|
||||
const keys = Array.isArray(key) ? key : [key];
|
||||
for (const key of keys) {
|
||||
if ((alt && !isAltPressed) || (!alt && isAltPressed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((meta && !isMetaPressed) || (!meta && isMetaPressed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((shift && !isShiftPressed) || (!shift && isShiftPressed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (key !== e.keyCode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
for (const [definition, callback] of this._globalKeyMap) {
|
||||
executeHotKey(e, definition, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,7 +622,6 @@ class App extends PureComponent {
|
||||
// Bind mouse and key handlers
|
||||
document.addEventListener('mouseup', this._handleMouseUp);
|
||||
document.addEventListener('mousemove', this._handleMouseMove);
|
||||
document.addEventListener('keydown', this._handleKeyDown);
|
||||
this._setGlobalKeyMap();
|
||||
|
||||
// Update title
|
||||
@@ -797,55 +710,56 @@ class App extends PureComponent {
|
||||
// Remove mouse and key handlers
|
||||
document.removeEventListener('mouseup', this._handleMouseUp);
|
||||
document.removeEventListener('mousemove', this._handleMouseMove);
|
||||
document.removeEventListener('keydown', this._handleKeyDown);
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<div className="app">
|
||||
<Wrapper
|
||||
{...this.props}
|
||||
ref={this._setWrapperRef}
|
||||
paneWidth={this.state.paneWidth}
|
||||
paneHeight={this.state.paneHeight}
|
||||
sidebarWidth={this.state.sidebarWidth}
|
||||
handleCreateRequestForWorkspace={this._requestCreateForWorkspace}
|
||||
handleSetRequestGroupCollapsed={this._handleSetRequestGroupCollapsed}
|
||||
handleActivateRequest={this._handleSetActiveRequest}
|
||||
handleSetRequestPaneRef={this._setRequestPaneRef}
|
||||
handleSetResponsePaneRef={this._setResponsePaneRef}
|
||||
handleSetSidebarRef={this._setSidebarRef}
|
||||
handleStartDragSidebar={this._startDragSidebar}
|
||||
handleResetDragSidebar={this._resetDragSidebar}
|
||||
handleStartDragPaneHorizontal={this._startDragPaneHorizontal}
|
||||
handleStartDragPaneVertical={this._startDragPaneVertical}
|
||||
handleResetDragPaneHorizontal={this._resetDragPaneHorizontal}
|
||||
handleResetDragPaneVertical={this._resetDragPaneVertical}
|
||||
handleCreateRequest={this._requestCreate}
|
||||
handleRender={this._handleRenderText}
|
||||
handleGetRenderContext={this._handleGetRenderContext}
|
||||
handleDuplicateRequest={this._requestDuplicate}
|
||||
handleDuplicateRequestGroup={this._requestGroupDuplicate}
|
||||
handleDuplicateWorkspace={this._workspaceDuplicate}
|
||||
handleCreateRequestGroup={this._requestGroupCreate}
|
||||
handleGenerateCode={this._handleGenerateCode}
|
||||
handleGenerateCodeForActiveRequest={this._handleGenerateCodeForActiveRequest}
|
||||
handleCopyAsCurl={this._handleCopyAsCurl}
|
||||
handleSetResponsePreviewMode={this._handleSetResponsePreviewMode}
|
||||
handleSetResponseFilter={this._handleSetResponseFilter}
|
||||
handleSendRequestWithEnvironment={this._handleSendRequestWithEnvironment}
|
||||
handleSendAndDownloadRequestWithEnvironment={this._handleSendAndDownloadRequestWithEnvironment}
|
||||
handleSetActiveResponse={this._handleSetActiveResponse}
|
||||
handleSetActiveRequest={this._handleSetActiveRequest}
|
||||
handleSetActiveEnvironment={this._handleSetActiveEnvironment}
|
||||
handleSetSidebarFilter={this._handleSetSidebarFilter}
|
||||
handleToggleMenuBar={this._handleToggleMenuBar}
|
||||
/>
|
||||
<Toast/>
|
||||
<KeydownBinder onKeydown={this._handleKeyDown}>
|
||||
<div className="app">
|
||||
<Wrapper
|
||||
{...this.props}
|
||||
ref={this._setWrapperRef}
|
||||
paneWidth={this.state.paneWidth}
|
||||
paneHeight={this.state.paneHeight}
|
||||
sidebarWidth={this.state.sidebarWidth}
|
||||
handleCreateRequestForWorkspace={this._requestCreateForWorkspace}
|
||||
handleSetRequestGroupCollapsed={this._handleSetRequestGroupCollapsed}
|
||||
handleActivateRequest={this._handleSetActiveRequest}
|
||||
handleSetRequestPaneRef={this._setRequestPaneRef}
|
||||
handleSetResponsePaneRef={this._setResponsePaneRef}
|
||||
handleSetSidebarRef={this._setSidebarRef}
|
||||
handleStartDragSidebar={this._startDragSidebar}
|
||||
handleResetDragSidebar={this._resetDragSidebar}
|
||||
handleStartDragPaneHorizontal={this._startDragPaneHorizontal}
|
||||
handleStartDragPaneVertical={this._startDragPaneVertical}
|
||||
handleResetDragPaneHorizontal={this._resetDragPaneHorizontal}
|
||||
handleResetDragPaneVertical={this._resetDragPaneVertical}
|
||||
handleCreateRequest={this._requestCreate}
|
||||
handleRender={this._handleRenderText}
|
||||
handleGetRenderContext={this._handleGetRenderContext}
|
||||
handleDuplicateRequest={this._requestDuplicate}
|
||||
handleDuplicateRequestGroup={this._requestGroupDuplicate}
|
||||
handleDuplicateWorkspace={this._workspaceDuplicate}
|
||||
handleCreateRequestGroup={this._requestGroupCreate}
|
||||
handleGenerateCode={this._handleGenerateCode}
|
||||
handleGenerateCodeForActiveRequest={this._handleGenerateCodeForActiveRequest}
|
||||
handleCopyAsCurl={this._handleCopyAsCurl}
|
||||
handleSetResponsePreviewMode={this._handleSetResponsePreviewMode}
|
||||
handleSetResponseFilter={this._handleSetResponseFilter}
|
||||
handleSendRequestWithEnvironment={this._handleSendRequestWithEnvironment}
|
||||
handleSendAndDownloadRequestWithEnvironment={this._handleSendAndDownloadRequestWithEnvironment}
|
||||
handleSetActiveResponse={this._handleSetActiveResponse}
|
||||
handleSetActiveRequest={this._handleSetActiveRequest}
|
||||
handleSetActiveEnvironment={this._handleSetActiveEnvironment}
|
||||
handleSetSidebarFilter={this._handleSetSidebarFilter}
|
||||
handleToggleMenuBar={this._handleToggleMenuBar}
|
||||
/>
|
||||
<Toast/>
|
||||
|
||||
{/* Block all mouse activity by showing an overlay while dragging */}
|
||||
{this.state.showDragOverlay ? <div className="blocker-overlay"></div> : null}
|
||||
</div>
|
||||
{/* Block all mouse activity by showing an overlay while dragging */}
|
||||
{this.state.showDragOverlay ? <div className="blocker-overlay"></div> : null}
|
||||
</div>
|
||||
</KeydownBinder>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@
|
||||
label {
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
padding-top: @padding-xs;
|
||||
margin-top: @padding-xs;
|
||||
padding-top: 0;
|
||||
|
||||
* {
|
||||
font-weight: normal;
|
||||
@@ -279,3 +280,24 @@ input[type="color"] {
|
||||
height: @line-height-xs !important;
|
||||
padding: @padding-xxs !important;
|
||||
}
|
||||
|
||||
label {
|
||||
// So we can highlight from CSS checkbox selector
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
input[type="checkbox"]:focus,
|
||||
input[type="radio"]:focus {
|
||||
&::before {
|
||||
z-index: -1;
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
top: -@padding-xxs;
|
||||
bottom: -@padding-xxs;
|
||||
left: -@padding-xxs;
|
||||
right: -@padding-xxs;
|
||||
border-radius: @radius-sm;
|
||||
background-color: var(--hl-sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,18 +242,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Active sidebar request
|
||||
&.sidebar__item--request > button {
|
||||
border-left: @padding-xs solid transparent;
|
||||
& > button {
|
||||
border-left: @padding-xxs solid transparent;
|
||||
}
|
||||
|
||||
&.sidebar__item--active.sidebar__item--request > button,
|
||||
&.sidebar__item--active.sidebar__item--request > .sidebar__actions,
|
||||
&:hover > button,
|
||||
& > button:focus,
|
||||
&:hover .sidebar__actions {
|
||||
transition: background-color 130ms ease-out;
|
||||
&.sidebar__item--active.sidebar__item--request,
|
||||
&:focus,
|
||||
&:hover {
|
||||
background-color: @hl-xs;
|
||||
transition: background-color 0ms;
|
||||
}
|
||||
|
||||
& > button:focus {
|
||||
border-left-color: var(--color-surprise);
|
||||
}
|
||||
|
||||
.tag {
|
||||
@@ -287,31 +288,31 @@
|
||||
// Padding for nested folders
|
||||
|
||||
.sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md;
|
||||
padding-left: @padding-sm;
|
||||
}
|
||||
|
||||
.sidebar__list .sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md * 2;
|
||||
padding-left: @padding-sm + @padding-md * 1;
|
||||
}
|
||||
|
||||
.sidebar__list .sidebar__list .sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md * 3;
|
||||
padding-left: @padding-sm + @padding-md * 2;
|
||||
}
|
||||
|
||||
.sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md * 4;
|
||||
padding-left: @padding-sm + @padding-md * 3;
|
||||
}
|
||||
|
||||
.sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md * 5;
|
||||
padding-left: @padding-sm + @padding-md * 4;
|
||||
}
|
||||
|
||||
.sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md * 6;
|
||||
padding-left: @padding-sm + @padding-md * 5;
|
||||
}
|
||||
|
||||
.sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__list .sidebar__clickable {
|
||||
padding-left: @padding-md * 7;
|
||||
padding-left: @padding-sm + @padding-md * 6;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~ //
|
||||
@@ -332,6 +333,7 @@
|
||||
}
|
||||
|
||||
& > button:hover,
|
||||
& .dropdown > button:focus,
|
||||
& > .dropdown:hover > button {
|
||||
color: var(--color-font);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user