mirror of https://github.com/apache/superset.git
[SQL Lab] moving the db/schema/table select to the left (#1038)
This commit is contained in:
parent
fc1e63761c
commit
561828c2f8
|
@ -5,8 +5,6 @@ import { bindActionCreators } from 'redux';
|
||||||
import * as Actions from '../actions';
|
import * as Actions from '../actions';
|
||||||
import QueryLink from './QueryLink';
|
import QueryLink from './QueryLink';
|
||||||
|
|
||||||
import 'react-select/dist/react-select.css';
|
|
||||||
|
|
||||||
const LeftPane = (props) => {
|
const LeftPane = (props) => {
|
||||||
let queryElements;
|
let queryElements;
|
||||||
if (props.workspaceQueries.length > 0) {
|
if (props.workspaceQueries.length > 0) {
|
||||||
|
|
|
@ -5,9 +5,6 @@ import { bindActionCreators } from 'redux';
|
||||||
import * as Actions from '../actions';
|
import * as Actions from '../actions';
|
||||||
import shortid from 'shortid';
|
import shortid from 'shortid';
|
||||||
|
|
||||||
// CSS
|
|
||||||
import 'react-select/dist/react-select.css';
|
|
||||||
|
|
||||||
class QueryLink extends React.Component {
|
class QueryLink extends React.Component {
|
||||||
popTab() {
|
popTab() {
|
||||||
const qe = {
|
const qe = {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Alert, Panel, Tab, Tabs } from 'react-bootstrap';
|
import { Alert, Tab, Tabs } from 'react-bootstrap';
|
||||||
import QueryHistory from './QueryHistory';
|
import QueryHistory from './QueryHistory';
|
||||||
import ResultSet from './ResultSet';
|
import ResultSet from './ResultSet';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
@ -21,16 +21,12 @@ const SouthPane = function (props) {
|
||||||
return (
|
return (
|
||||||
<Tabs bsStyle="tabs">
|
<Tabs bsStyle="tabs">
|
||||||
<Tab title="Results" eventKey={1}>
|
<Tab title="Results" eventKey={1}>
|
||||||
<Panel>
|
<div style={{ overflow: 'auto' }}>
|
||||||
<div style={{ overflow: 'auto' }}>
|
{results}
|
||||||
{results}
|
</div>
|
||||||
</div>
|
|
||||||
</Panel>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Query History" eventKey={2}>
|
<Tab title="Query History" eventKey={2}>
|
||||||
<Panel>
|
<QueryHistory />
|
||||||
<QueryHistory />
|
|
||||||
</Panel>
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React from 'react';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
|
Col,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
Form,
|
Form,
|
||||||
|
@ -12,6 +13,7 @@ import {
|
||||||
Label,
|
Label,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
OverlayTrigger,
|
OverlayTrigger,
|
||||||
|
Row,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
} from 'react-bootstrap';
|
} from 'react-bootstrap';
|
||||||
|
|
||||||
|
@ -29,10 +31,7 @@ import ButtonWithTooltip from './ButtonWithTooltip';
|
||||||
import SouthPane from './SouthPane';
|
import SouthPane from './SouthPane';
|
||||||
import Timer from './Timer';
|
import Timer from './Timer';
|
||||||
|
|
||||||
import SqlEditorTopToolbar from './SqlEditorTopToolbar';
|
import SqlEditorLeft from './SqlEditorLeft';
|
||||||
|
|
||||||
// CSS
|
|
||||||
import 'react-select/dist/react-select.css';
|
|
||||||
|
|
||||||
class SqlEditor extends React.Component {
|
class SqlEditor extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -246,23 +245,29 @@ class SqlEditor extends React.Component {
|
||||||
<div className="SqlEditor">
|
<div className="SqlEditor">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<SqlEditorTopToolbar queryEditor={this.props.queryEditor} />
|
<Row>
|
||||||
<AceEditor
|
<Col md={3}>
|
||||||
mode="sql"
|
<SqlEditorLeft queryEditor={this.props.queryEditor} />
|
||||||
name={this.props.queryEditor.id}
|
</Col>
|
||||||
theme="github"
|
<Col md={9}>
|
||||||
minLines={5}
|
<AceEditor
|
||||||
maxLines={30}
|
mode="sql"
|
||||||
onChange={this.textChange.bind(this)}
|
name={this.props.queryEditor.id}
|
||||||
height="200px"
|
theme="github"
|
||||||
width="100%"
|
minLines={5}
|
||||||
editorProps={{ $blockScrolling: true }}
|
maxLines={30}
|
||||||
enableBasicAutocompletion
|
onChange={this.textChange.bind(this)}
|
||||||
value={this.props.queryEditor.sql}
|
height="200px"
|
||||||
/>
|
width="100%"
|
||||||
{editorBottomBar}
|
editorProps={{ $blockScrolling: true }}
|
||||||
<br />
|
enableBasicAutocompletion
|
||||||
<SouthPane latestQuery={this.props.latestQuery} sqlEditor={this} />
|
value={this.props.queryEditor.sql}
|
||||||
|
/>
|
||||||
|
{editorBottomBar}
|
||||||
|
<br />
|
||||||
|
<SouthPane latestQuery={this.props.latestQuery} sqlEditor={this} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
const $ = window.$ = require('jquery');
|
const $ = window.$ = require('jquery');
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Label, OverlayTrigger, Popover } from 'react-bootstrap';
|
|
||||||
|
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as Actions from '../actions';
|
import * as Actions from '../actions';
|
||||||
import shortid from 'shortid';
|
import shortid from 'shortid';
|
||||||
import Select from 'react-select';
|
import Select from 'react-select';
|
||||||
import Link from './Link';
|
import TableElement from './TableElement';
|
||||||
|
|
||||||
// CSS
|
|
||||||
import 'react-select/dist/react-select.css';
|
|
||||||
|
|
||||||
class SqlEditorTopToolbar extends React.Component {
|
class SqlEditorTopToolbar extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -123,7 +120,6 @@ class SqlEditorTopToolbar extends React.Component {
|
||||||
schema: qe.schema,
|
schema: qe.schema,
|
||||||
columns: data.columns,
|
columns: data.columns,
|
||||||
expanded: true,
|
expanded: true,
|
||||||
showPopup: true,
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.fail(() => {
|
||||||
|
@ -135,76 +131,9 @@ class SqlEditorTopToolbar extends React.Component {
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
const tables = this.props.tables.filter((t) => (t.queryEditorId === this.props.queryEditor.id));
|
const tables = this.props.tables.filter((t) => (t.queryEditorId === this.props.queryEditor.id));
|
||||||
const tablesEls = tables.map((table) => {
|
|
||||||
let cols = [];
|
|
||||||
if (table.columns) {
|
|
||||||
cols = table.columns.map((col) => (
|
|
||||||
<div className="clearfix">
|
|
||||||
<div className="pull-left m-r-10">{col.name}</div>
|
|
||||||
<div className="pull-right text-muted"> {col.type}</div>
|
|
||||||
</div>
|
|
||||||
));
|
|
||||||
}
|
|
||||||
const popoverId = 'tblPopover_' + table.name;
|
|
||||||
const popoverTop = (
|
|
||||||
<div className="clearfix">
|
|
||||||
<div className="pull-left">
|
|
||||||
<Link
|
|
||||||
className="fa fa-pencil"
|
|
||||||
onClick={this.selectStar.bind(this, table)}
|
|
||||||
tooltip="Overwrite text in editor with a query on this table"
|
|
||||||
placement="left"
|
|
||||||
href="#"
|
|
||||||
/>
|
|
||||||
<Link
|
|
||||||
className="fa fa-plus-circle"
|
|
||||||
onClick={this.popTab.bind(this, table)}
|
|
||||||
tooltip="Run query in a new tab"
|
|
||||||
placement="left"
|
|
||||||
href="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="pull-right">
|
|
||||||
<Link
|
|
||||||
className="fa fa-close"
|
|
||||||
onClick={this.closePopover.bind(this, popoverId)}
|
|
||||||
href="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
const popover = (
|
|
||||||
<Popover
|
|
||||||
id={popoverId}
|
|
||||||
className="tablePopover"
|
|
||||||
title={popoverTop}
|
|
||||||
>
|
|
||||||
{cols}
|
|
||||||
</Popover>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Label className="m-r-5 table-label" style={{ fontSize: '100%' }}>
|
|
||||||
<OverlayTrigger
|
|
||||||
trigger="click"
|
|
||||||
placement="bottom"
|
|
||||||
overlay={popover}
|
|
||||||
ref={popoverId}
|
|
||||||
>
|
|
||||||
<span className="m-r-5" style={{ cursor: 'pointer' }}>
|
|
||||||
{table.name}
|
|
||||||
</span>
|
|
||||||
</OverlayTrigger>
|
|
||||||
<i
|
|
||||||
className="fa fa-close"
|
|
||||||
style={{ cursor: 'pointer' }}
|
|
||||||
onClick={this.props.actions.removeTable.bind(this, table)}
|
|
||||||
/>
|
|
||||||
</Label>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return (
|
return (
|
||||||
<div className="clearfix sql-toolbar">
|
<div className="clearfix sql-toolbar">
|
||||||
<div className="pull-left m-r-5">
|
<div>
|
||||||
<Select
|
<Select
|
||||||
name="select-db"
|
name="select-db"
|
||||||
placeholder="[Database]"
|
placeholder="[Database]"
|
||||||
|
@ -215,7 +144,7 @@ class SqlEditorTopToolbar extends React.Component {
|
||||||
onChange={this.changeDb.bind(this)}
|
onChange={this.changeDb.bind(this)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pull-left m-r-5">
|
<div className="m-t-5">
|
||||||
<Select
|
<Select
|
||||||
name="select-schema"
|
name="select-schema"
|
||||||
placeholder="[Schema]"
|
placeholder="[Schema]"
|
||||||
|
@ -226,7 +155,7 @@ class SqlEditorTopToolbar extends React.Component {
|
||||||
onChange={this.changeSchema.bind(this)}
|
onChange={this.changeSchema.bind(this)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pull-left m-r-5">
|
<div className="m-t-5">
|
||||||
<Select
|
<Select
|
||||||
name="select-table"
|
name="select-table"
|
||||||
ref="selectTable"
|
ref="selectTable"
|
||||||
|
@ -238,8 +167,9 @@ class SqlEditorTopToolbar extends React.Component {
|
||||||
options={this.state.tableOptions}
|
options={this.state.tableOptions}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="pull-left m-r-5">
|
<hr />
|
||||||
{tablesEls}
|
<div className="m-t-5">
|
||||||
|
{tables.map((table) => <TableElement table={table} />)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
|
@ -6,10 +6,7 @@ import { bindActionCreators } from 'redux';
|
||||||
import * as Actions from '../actions';
|
import * as Actions from '../actions';
|
||||||
import shortid from 'shortid';
|
import shortid from 'shortid';
|
||||||
|
|
||||||
// CSS
|
class TableElement extends React.Component {
|
||||||
import 'react-select/dist/react-select.css';
|
|
||||||
|
|
||||||
class TableWorkspaceElement extends React.Component {
|
|
||||||
selectStar() {
|
selectStar() {
|
||||||
let cols = '';
|
let cols = '';
|
||||||
this.props.table.columns.forEach((col, i) => {
|
this.props.table.columns.forEach((col, i) => {
|
||||||
|
@ -31,35 +28,37 @@ class TableWorkspaceElement extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
let metadata = null;
|
let metadata = null;
|
||||||
let buttonToggle;
|
let buttonToggle;
|
||||||
if (!this.props.table.expanded) {
|
if (this.props.table.expanded) {
|
||||||
buttonToggle = (
|
|
||||||
<Link
|
|
||||||
href="#"
|
|
||||||
onClick={this.props.actions.expandTable.bind(this, this.props.table)}
|
|
||||||
placement="right"
|
|
||||||
tooltip="Collapse the table's structure information"
|
|
||||||
>
|
|
||||||
<i className="fa fa-minus" /> {this.props.table.name}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
metadata = this.props.table.columns.map((col) =>
|
|
||||||
<div className="clearfix">
|
|
||||||
<span className="pull-left">{col.name}</span>
|
|
||||||
<span className="pull-right">{col.type}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
metadata = (
|
|
||||||
<div style={{ 'margin-bottom': '5px' }}>{metadata}</div>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
buttonToggle = (
|
buttonToggle = (
|
||||||
<Link
|
<Link
|
||||||
href="#"
|
href="#"
|
||||||
onClick={this.props.actions.collapseTable.bind(this, this.props.table)}
|
onClick={this.props.actions.collapseTable.bind(this, this.props.table)}
|
||||||
placement="right"
|
placement="right"
|
||||||
|
tooltip="Collapse the table's structure information"
|
||||||
|
>
|
||||||
|
{this.props.table.name} <i className="fa fa-caret-up" />
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
metadata = (
|
||||||
|
<div>
|
||||||
|
{this.props.table.columns.map((col) => (
|
||||||
|
<div className="clearfix">
|
||||||
|
<span className="pull-left m-l-5">{col.name}</span>
|
||||||
|
<span className="pull-right">{col.type}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
buttonToggle = (
|
||||||
|
<Link
|
||||||
|
href="#"
|
||||||
|
onClick={this.props.actions.expandTable.bind(this, this.props.table)}
|
||||||
|
placement="right"
|
||||||
tooltip="Expand the table's structure information"
|
tooltip="Expand the table's structure information"
|
||||||
>
|
>
|
||||||
<i className="fa fa-plus" /> {this.props.table.name}
|
{this.props.table.name} <i className="fa fa-caret-down" />
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -68,13 +67,19 @@ class TableWorkspaceElement extends React.Component {
|
||||||
{buttonToggle}
|
{buttonToggle}
|
||||||
<ButtonGroup className="ws-el-controls pull-right">
|
<ButtonGroup className="ws-el-controls pull-right">
|
||||||
<Link
|
<Link
|
||||||
className="fa fa-play"
|
className="fa fa-pencil m-l-2"
|
||||||
onClick={this.selectStar.bind(this)}
|
onClick={this.selectStar.bind(this)}
|
||||||
tooltip="Run query in a new tab"
|
tooltip="Run query in a new tab"
|
||||||
href="#"
|
href="#"
|
||||||
/>
|
/>
|
||||||
<Link
|
<Link
|
||||||
className="fa fa-trash"
|
className="fa fa-plus-circle m-l-2"
|
||||||
|
onClick={this.selectStar.bind(this)}
|
||||||
|
tooltip="Run query in a new tab"
|
||||||
|
href="#"
|
||||||
|
/>
|
||||||
|
<Link
|
||||||
|
className="fa fa-trash m-l-2"
|
||||||
onClick={this.props.actions.removeTable.bind(this, this.props.table)}
|
onClick={this.props.actions.removeTable.bind(this, this.props.table)}
|
||||||
tooltip="Remove from workspace"
|
tooltip="Remove from workspace"
|
||||||
href="#"
|
href="#"
|
||||||
|
@ -85,11 +90,11 @@ class TableWorkspaceElement extends React.Component {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TableWorkspaceElement.propTypes = {
|
TableElement.propTypes = {
|
||||||
table: React.PropTypes.object,
|
table: React.PropTypes.object,
|
||||||
actions: React.PropTypes.object,
|
actions: React.PropTypes.object,
|
||||||
};
|
};
|
||||||
TableWorkspaceElement.defaultProps = {
|
TableElement.defaultProps = {
|
||||||
table: null,
|
table: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -98,5 +103,4 @@ function mapDispatchToProps(dispatch) {
|
||||||
actions: bindActionCreators(Actions, dispatch),
|
actions: bindActionCreators(Actions, dispatch),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
export default connect(null, mapDispatchToProps)(TableWorkspaceElement);
|
export default connect(null, mapDispatchToProps)(TableElement);
|
||||||
|
|
|
@ -6,7 +6,6 @@ import React from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import * as Actions from './actions';
|
import * as Actions from './actions';
|
||||||
|
|
||||||
import LeftPane from './components/LeftPane';
|
|
||||||
import TabbedSqlEditors from './components/TabbedSqlEditors';
|
import TabbedSqlEditors from './components/TabbedSqlEditors';
|
||||||
import QueryAutoRefresh from './components/QueryAutoRefresh';
|
import QueryAutoRefresh from './components/QueryAutoRefresh';
|
||||||
import Alerts from './components/Alerts';
|
import Alerts from './components/Alerts';
|
||||||
|
@ -37,12 +36,9 @@ const App = function (props) {
|
||||||
<QueryAutoRefresh />
|
<QueryAutoRefresh />
|
||||||
<Alerts alerts={props.alerts} />
|
<Alerts alerts={props.alerts} />
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-9">
|
<div className="col-md-12">
|
||||||
<TabbedSqlEditors />
|
<TabbedSqlEditors />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-md-3">
|
|
||||||
<LeftPane />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -72,9 +72,18 @@ div.Workspace {
|
||||||
.m-l-1 {
|
.m-l-1 {
|
||||||
margin-left: 1px;
|
margin-left: 1px;
|
||||||
}
|
}
|
||||||
|
.m-l-2 {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
.m-r-10 {
|
.m-r-10 {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
.m-l-10 {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
.m-l-5 {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
.m-b-10 {
|
.m-b-10 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
@ -132,7 +141,9 @@ div.Workspace {
|
||||||
max-height: 600px;
|
max-height: 600px;
|
||||||
box-shadow: rgba(0, 0, 0, 0.8) 5px 5px 25px
|
box-shadow: rgba(0, 0, 0, 0.8) 5px 5px 25px
|
||||||
}
|
}
|
||||||
|
.SqlLab {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
.SqlLab pre {
|
.SqlLab pre {
|
||||||
padding: 0px !important;
|
padding: 0px !important;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
@ -217,7 +228,7 @@ div.tablePopover:hover {
|
||||||
|
|
||||||
.ace_editor {
|
.ace_editor {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
margin: 10px 0;
|
margin: 0px 0px 10px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Select-menu-outer {
|
.Select-menu-outer {
|
||||||
|
@ -227,3 +238,11 @@ div.tablePopover:hover {
|
||||||
.ace_content {
|
.ace_content {
|
||||||
background-color: #f4f4f4;
|
background-color: #f4f4f4;
|
||||||
}
|
}
|
||||||
|
.ws-el > .ws-el-controls {
|
||||||
|
opacity: 0;
|
||||||
|
transition: visibility 0s, opacity 0.3s linear;
|
||||||
|
}
|
||||||
|
.ws-el:hover > .ws-el-controls {
|
||||||
|
opacity: 1;
|
||||||
|
transition: visibility 0s, opacity 0.3s linear;
|
||||||
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
require('../stylesheets/less/index.less');
|
require('../stylesheets/less/index.less');
|
||||||
|
require('../stylesheets/react-select/select.less');
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
@select-input-border-radius: 4px;
|
@select-input-border-radius: 4px;
|
||||||
@select-input-border-focus: @select-primary-color;
|
@select-input-border-focus: @select-primary-color;
|
||||||
@select-input-border-width: 1px;
|
@select-input-border-width: 1px;
|
||||||
@select-input-height: 36px;
|
@select-input-height: 30px;
|
||||||
@select-input-internal-height: (@select-input-height - (@select-input-border-width * 2));
|
@select-input-internal-height: (@select-input-height - (@select-input-border-width * 2));
|
||||||
@select-input-placeholder: #aaa;
|
@select-input-placeholder: #aaa;
|
||||||
@select-text-color: #333;
|
@select-text-color: #333;
|
||||||
|
|
Loading…
Reference in New Issue