From commits-return-822-archive-asf-public=cust-asf.ponee.io@superset.incubator.apache.org Wed Mar 28 00:54:58 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 39DAF180671 for ; Wed, 28 Mar 2018 00:54:58 +0200 (CEST) Received: (qmail 67775 invoked by uid 500); 27 Mar 2018 22:54:57 -0000 Mailing-List: contact commits-help@superset.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@superset.incubator.apache.org Delivered-To: mailing list commits@superset.incubator.apache.org Received: (qmail 67766 invoked by uid 99); 27 Mar 2018 22:54:57 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 27 Mar 2018 22:54:57 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id ACB8380A49; Tue, 27 Mar 2018 22:54:56 +0000 (UTC) Date: Tue, 27 Mar 2018 22:54:56 +0000 To: "commits@superset.apache.org" Subject: [incubator-superset] branch master updated: Hotkeys in SQL Lab (#4680) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <152219129652.5510.17294883874133200174@gitbox.apache.org> From: maximebeauchemin@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: incubator-superset X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: deb211154df472329400452f747059ea83fd181b X-Git-Newrev: f510956da210e9edc24597b1cced27460eed07ac X-Git-Rev: f510956da210e9edc24597b1cced27460eed07ac X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. maximebeauchemin pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-superset.git The following commit(s) were added to refs/heads/master by this push: new f510956 Hotkeys in SQL Lab (#4680) f510956 is described below commit f510956da210e9edc24597b1cced27460eed07ac Author: Maxime Beauchemin AuthorDate: Tue Mar 27 15:54:54 2018 -0700 Hotkeys in SQL Lab (#4680) * Hotkeys * Making it work in AceEditor * Addressing comments --- superset/assets/javascripts/SqlLab/actions.js | 7 ++- .../SqlLab/components/AceEditorWrapper.jsx | 15 ++++- .../javascripts/SqlLab/components/SqlEditor.jsx | 64 ++++++++++++++++------ superset/assets/javascripts/components/Hotkeys.jsx | 53 ++++++++++++++++++ 4 files changed, 118 insertions(+), 21 deletions(-) diff --git a/superset/assets/javascripts/SqlLab/actions.js b/superset/assets/javascripts/SqlLab/actions.js index d1fbfea..04a9a5e 100644 --- a/superset/assets/javascripts/SqlLab/actions.js +++ b/superset/assets/javascripts/SqlLab/actions.js @@ -196,8 +196,11 @@ export function setDatabases(databases) { } export function addQueryEditor(queryEditor) { - const newQe = Object.assign({}, queryEditor, { id: shortid.generate() }); - return { type: ADD_QUERY_EDITOR, queryEditor: newQe }; + const newQueryEditor = { + ...queryEditor, + id: shortid.generate(), + }; + return { type: ADD_QUERY_EDITOR, queryEditor: newQueryEditor }; } export function cloneQueryToNewTab(query) { diff --git a/superset/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx b/superset/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx index a66d80e..24e9e25 100644 --- a/superset/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx +++ b/superset/assets/javascripts/SqlLab/components/AceEditorWrapper.jsx @@ -28,16 +28,19 @@ const sqlWords = sqlKeywords.map(s => ({ const propTypes = { actions: PropTypes.object.isRequired, onBlur: PropTypes.func, - onAltEnter: PropTypes.func, sql: PropTypes.string.isRequired, tables: PropTypes.array, queryEditor: PropTypes.object.isRequired, height: PropTypes.string, + hotkeys: PropTypes.arrayOf(PropTypes.shape({ + key: PropTypes.string.isRequired, + descr: PropTypes.string.isRequired, + func: PropTypes.func.isRequired, + })), }; const defaultProps = { onBlur: () => {}, - onAltEnter: () => {}, tables: [], }; @@ -67,7 +70,6 @@ class AceEditorWrapper extends React.PureComponent { } onAltEnter() { this.props.onBlur(this.state.sql); - this.props.onAltEnter(); } onEditorLoad(editor) { editor.commands.addCommand({ @@ -77,6 +79,13 @@ class AceEditorWrapper extends React.PureComponent { this.onAltEnter(); }, }); + this.props.hotkeys.forEach((keyConfig) => { + editor.commands.addCommand({ + name: keyConfig.name, + bindKey: { win: keyConfig.key, mac: keyConfig.key }, + exec: keyConfig.func, + }); + }); editor.$blockScrolling = Infinity; // eslint-disable-line no-param-reassign editor.selection.on('changeSelection', () => { const selectedText = editor.getSelectedText(); diff --git a/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx b/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx index 5a2cd04..57be300 100644 --- a/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx +++ b/superset/assets/javascripts/SqlLab/components/SqlEditor.jsx @@ -21,6 +21,7 @@ import SouthPane from './SouthPane'; import SaveQuery from './SaveQuery'; import ShareQuery from './ShareQuery'; import Timer from '../../components/Timer'; +import Hotkeys from '../../components/Hotkeys'; import SqlEditorLeftBar from './SqlEditorLeftBar'; import AceEditorWrapper from './AceEditorWrapper'; import { STATE_BSSTYLE_MAP } from '../constants'; @@ -46,7 +47,6 @@ const defaultProps = { hideLeftBar: false, }; - class SqlEditor extends React.PureComponent { constructor(props) { super(props); @@ -57,6 +57,8 @@ class SqlEditor extends React.PureComponent { this.onResize = this.onResize.bind(this); this.throttledResize = throttle(this.onResize, 250); + this.runQuery = this.runQuery.bind(this); + this.stopQuery = this.stopQuery.bind(this); } componentWillMount() { if (this.state.autorun) { @@ -86,18 +88,39 @@ class SqlEditor extends React.PureComponent { this.props.actions.persistEditorHeight(this.props.queryEditor, this.refs.ace.clientHeight); } } + getHotkeyConfig() { + return [ + { + name: 'runQuery', + key: 'ctrl+r', + descr: 'Run query', + func: this.runQuery, + }, + { + name: 'newTab', + key: 'ctrl+t', + descr: 'New tab', + func: () => { + this.props.actions.addQueryEditor({ + ...this.props.queryEditor, + title: t('Untitled Query'), + sql: '', + }); + }, + }, + { + name: 'stopQuery', + key: 'ctrl+x', + descr: 'Stop query', + func: this.stopQuery, + }, + ]; + } setQueryEditorSql(sql) { this.props.actions.queryEditorSetSql(this.props.queryEditor, sql); } - runQuery(runAsync = false) { - if (!this.props.queryEditor.sql) { - return; - } - let effectiveRunAsync = runAsync; - if (!this.props.database.allow_run_sync) { - effectiveRunAsync = true; - } - this.startQuery(effectiveRunAsync); + runQuery() { + this.startQuery(!this.props.database.allow_run_sync); } startQuery(runAsync = false, ctas = false) { const qe = this.props.queryEditor; @@ -116,7 +139,9 @@ class SqlEditor extends React.PureComponent { this.props.actions.setActiveSouthPaneTab('Results'); } stopQuery() { - this.props.actions.postStopQuery(this.props.latestQuery); + if (this.props.latestQuery && this.props.latestQuery.state === 'running') { + this.props.actions.postStopQuery(this.props.latestQuery); + } } createTableAs() { this.startQuery(true, true); @@ -128,7 +153,7 @@ class SqlEditor extends React.PureComponent { const horizontalScrollbarHeight = 25; return parseInt(this.props.getHeight(), 10) - horizontalScrollbarHeight; } - renderEditorBottomBar() { + renderEditorBottomBar(hotkeys) { let ctasControls; if (this.props.database && this.props.database.allow_ctas) { const ctasToolTip = t('Create table as with query results'); @@ -181,9 +206,9 @@ class SqlEditor extends React.PureComponent { allowAsync={this.props.database ? this.props.database.allow_run_async : false} dbId={qe.dbId} queryState={this.props.latestQuery && this.props.latestQuery.state} - runQuery={this.runQuery.bind(this)} + runQuery={this.runQuery} selectedText={qe.selectedText} - stopQuery={this.stopQuery.bind(this)} + stopQuery={this.stopQuery} /> @@ -200,6 +225,12 @@ class SqlEditor extends React.PureComponent { {ctasControls} + + +
@@ -226,6 +257,7 @@ class SqlEditor extends React.PureComponent { render() { const height = this.sqlEditorHeight(); const defaultNorthHeight = this.props.queryEditor.height || 200; + const hotkeys = this.getHotkeyConfig(); return (
- {this.renderEditorBottomBar()} + {this.renderEditorBottomBar(hotkeys)}
diff --git a/superset/assets/javascripts/components/Hotkeys.jsx b/superset/assets/javascripts/components/Hotkeys.jsx new file mode 100644 index 0000000..f6ea114 --- /dev/null +++ b/superset/assets/javascripts/components/Hotkeys.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { OverlayTrigger, Popover } from 'react-bootstrap'; +import { Table } from 'reactable'; + +import Mousetrap from 'mousetrap'; + +const propTypes = { + hotkeys: PropTypes.arrayOf(PropTypes.shape({ + key: PropTypes.string.isRequired, + descr: PropTypes.string.isRequired, + func: PropTypes.func.isRequired, + })).isRequired, + header: PropTypes.string, +}; + +const defaultProps = { + hotkeys: [], +}; + +export default class Hotkeys extends React.PureComponent { + componentDidMount() { + this.props.hotkeys.forEach((keyConfig) => { + Mousetrap.bind([keyConfig.key], keyConfig.func); + }); + } + renderPopover() { + return ( + + ({ + Key: keyConfig.key, + Action: keyConfig.descr, + }))} + /> + ); + } + render() { + return ( + + + + ); + } +} + +Hotkeys.propTypes = propTypes; +Hotkeys.defaultProps = defaultProps; -- To stop receiving notification emails like this one, please contact maximebeauchemin@apache.org.