mirror of https://github.com/apache/superset.git
Relayout SQL Editor (#6872)
* Relayout SQL Editor - Refactor SQL editor to remove usage of bootstrap col, row and collapse to simplify the layout - Replace the react-split-pane libraray with react-split to allow custom styling of the gutter area without sacrifice correctness of the ace editor height calculation - Rewrite the left pane animation via plain css transition and animate it to slide in and out - General code and css clean up * Smooth out the visual transition during dragging (cherry picked from commit 19f82b729c7a939f12b1c5da6022c0fd76fa3ec9) * Adjust how the height of the south pane is computed, fixing cypress tests
This commit is contained in:
parent
8302b9a276
commit
ec6657ab2d
|
@ -17339,6 +17339,15 @@
|
|||
"prop-types": "^15.5.7"
|
||||
}
|
||||
},
|
||||
"react-split": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-split/-/react-split-2.0.4.tgz",
|
||||
"integrity": "sha512-NBKm9MaqzG/00laMUaS8GS9RnItVSekNNwItgGLMbFTeUa9w4bIY8Co/LszNBnpza9n2am0MXIw3SmyiMnhs+w==",
|
||||
"requires": {
|
||||
"prop-types": "^15.5.7",
|
||||
"split.js": "^1.5.9"
|
||||
}
|
||||
},
|
||||
"react-split-pane": {
|
||||
"version": "0.1.85",
|
||||
"resolved": "https://registry.npmjs.org/react-split-pane/-/react-split-pane-0.1.85.tgz",
|
||||
|
@ -17380,6 +17389,17 @@
|
|||
"refractor": "^2.4.1"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.5.3.tgz",
|
||||
"integrity": "sha512-2DGFck6h99kLNr8pOFk+z4Soq3iISydwOFeeEVPjTN6+Y01CmvbWmnN02VuTWyFdnRtIDPe+wy2q6Ui8snBPZg==",
|
||||
"requires": {
|
||||
"dom-helpers": "^3.3.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"react-lifecycles-compat": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"react-virtualized": {
|
||||
"version": "9.19.1",
|
||||
"resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.19.1.tgz",
|
||||
|
@ -19391,6 +19411,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"split.js": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/split.js/-/split.js-1.5.10.tgz",
|
||||
"integrity": "sha512-/J52X5c4ZypVwu4WAhD8E1T9uXQtNokvG6mIBHauzyA1aKH6bmETVSv3RPjBXEz6Gcc4mIThgmjGQL39LD16jQ=="
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "http://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
|
|
|
@ -123,9 +123,10 @@
|
|||
"react-select": "1.2.1",
|
||||
"react-select-fast-filter-options": "^0.2.1",
|
||||
"react-sortable-hoc": "^0.8.3",
|
||||
"react-split-pane": "^0.1.66",
|
||||
"react-split": "^2.0.4",
|
||||
"react-sticky": "^6.0.2",
|
||||
"react-syntax-highlighter": "^7.0.4",
|
||||
"react-transition-group": "^2.5.3",
|
||||
"react-virtualized": "9.19.1",
|
||||
"react-virtualized-select": "^3.1.3",
|
||||
"reactable-arc": "0.14.42",
|
||||
|
|
|
@ -24,7 +24,7 @@ import { shallow } from 'enzyme';
|
|||
|
||||
import { STATUS_OPTIONS } from '../../../src/SqlLab/constants';
|
||||
import { initialState } from './fixtures';
|
||||
import SouthPane from '../../../src/SqlLab/components/SouthPane';
|
||||
import SouthPaneContainer, { SouthPane } from '../../../src/SqlLab/components/SouthPane';
|
||||
|
||||
describe('SouthPane', () => {
|
||||
const middlewares = [thunk];
|
||||
|
@ -42,11 +42,16 @@ describe('SouthPane', () => {
|
|||
};
|
||||
|
||||
const getWrapper = () => (
|
||||
shallow(<SouthPane {...mockedProps} />, {
|
||||
shallow(<SouthPaneContainer {...mockedProps} />, {
|
||||
context: { store },
|
||||
}).dive());
|
||||
|
||||
let wrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(SouthPane.prototype, 'getSouthPaneHeight').mockImplementation(() => 500);
|
||||
});
|
||||
|
||||
it('should render offline when the state is offline', () => {
|
||||
wrapper = getWrapper();
|
||||
wrapper.setProps({ offline: true });
|
||||
|
|
|
@ -38,6 +38,11 @@ describe('SqlEditor', () => {
|
|||
defaultQueryLimit: 1000,
|
||||
maxRow: 100000,
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
jest.spyOn(SqlEditor.prototype, 'getSqlEditorHeight').mockImplementation(() => 500);
|
||||
});
|
||||
|
||||
it('is valid', () => {
|
||||
expect(
|
||||
React.isValidElement(<SqlEditor {...mockedProps} />),
|
||||
|
|
|
@ -84,7 +84,7 @@ class App extends React.PureComponent {
|
|||
content = (
|
||||
<div>
|
||||
<QueryAutoRefresh />
|
||||
<TabbedSqlEditors getHeight={this.getHeight} />
|
||||
<TabbedSqlEditors />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -48,7 +48,24 @@ const defaultProps = {
|
|||
offline: false,
|
||||
};
|
||||
|
||||
class SouthPane extends React.PureComponent {
|
||||
export class SouthPane extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
height: props.height,
|
||||
};
|
||||
this.southPaneRef = React.createRef();
|
||||
this.getSouthPaneHeight = this.getSouthPaneHeight.bind(this);
|
||||
this.switchTab = this.switchTab.bind(this);
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
// south pane expands the entire height of the tab content on mount
|
||||
this.setState({ height: this.getSouthPaneHeight() });
|
||||
}
|
||||
// One layer of abstraction for easy spying in unit tests
|
||||
getSouthPaneHeight() {
|
||||
return this.southPaneRef.current.clientHeight;
|
||||
}
|
||||
switchTab(id) {
|
||||
this.props.actions.setActiveSouthPaneTab(id);
|
||||
}
|
||||
|
@ -59,7 +76,7 @@ class SouthPane extends React.PureComponent {
|
|||
{ STATUS_OPTIONS.offline }
|
||||
</Label>);
|
||||
}
|
||||
const innerTabHeight = this.props.height - 55;
|
||||
const innerTabHeight = this.state.height - 55;
|
||||
let latestQuery;
|
||||
const props = this.props;
|
||||
if (props.editorQueries.length > 0) {
|
||||
|
@ -98,12 +115,12 @@ class SouthPane extends React.PureComponent {
|
|||
));
|
||||
|
||||
return (
|
||||
<div className="SouthPane">
|
||||
<div className="SouthPane" ref={this.southPaneRef}>
|
||||
<Tabs
|
||||
bsStyle="tabs"
|
||||
id={shortid.generate()}
|
||||
activeKey={this.props.activeSouthPaneTab}
|
||||
onSelect={this.switchTab.bind(this)}
|
||||
onSelect={this.switchTab}
|
||||
>
|
||||
<Tab
|
||||
title={t('Results')}
|
||||
|
|
|
@ -17,21 +17,18 @@
|
|||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { CSSTransition } from 'react-transition-group';
|
||||
import PropTypes from 'prop-types';
|
||||
import { throttle } from 'lodash';
|
||||
import {
|
||||
Col,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Form,
|
||||
FormControl,
|
||||
Label,
|
||||
OverlayTrigger,
|
||||
Row,
|
||||
Tooltip,
|
||||
Collapse,
|
||||
} from 'react-bootstrap';
|
||||
import SplitPane from 'react-split-pane';
|
||||
import Split from 'react-split';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import Button from '../../components/Button';
|
||||
|
@ -47,9 +44,13 @@ import AceEditorWrapper from './AceEditorWrapper';
|
|||
import { STATE_BSSTYLE_MAP } from '../constants';
|
||||
import RunQueryActionButton from './RunQueryActionButton';
|
||||
|
||||
const SQL_TOOLBAR_HEIGHT = 51;
|
||||
const GUTTER_HEIGHT = 5;
|
||||
const INITIAL_NORTH_PERCENT = 30;
|
||||
const INITIAL_SOUTH_PERCENT = 70;
|
||||
|
||||
const propTypes = {
|
||||
actions: PropTypes.object.isRequired,
|
||||
getHeight: PropTypes.func.isRequired,
|
||||
database: PropTypes.object,
|
||||
latestQuery: PropTypes.object,
|
||||
tables: PropTypes.array.isRequired,
|
||||
|
@ -75,13 +76,18 @@ class SqlEditor extends React.PureComponent {
|
|||
ctas: '',
|
||||
sql: props.queryEditor.sql,
|
||||
};
|
||||
this.sqlEditorRef = React.createRef();
|
||||
this.northPaneRef = React.createRef();
|
||||
|
||||
this.onResize = this.onResize.bind(this);
|
||||
this.throttledResize = throttle(this.onResize, 250);
|
||||
this.onResizeStart = this.onResizeStart.bind(this);
|
||||
this.onResizeEnd = this.onResizeEnd.bind(this);
|
||||
this.runQuery = this.runQuery.bind(this);
|
||||
this.stopQuery = this.stopQuery.bind(this);
|
||||
this.onSqlChanged = this.onSqlChanged.bind(this);
|
||||
this.setQueryEditorSql = this.setQueryEditorSql.bind(this);
|
||||
this.queryPane = this.queryPane.bind(this);
|
||||
this.getAceEditorAndSouthPaneHeights = this.getAceEditorAndSouthPaneHeights.bind(this);
|
||||
this.getSqlEditorHeight = this.getSqlEditorHeight.bind(this);
|
||||
}
|
||||
componentWillMount() {
|
||||
if (this.state.autorun) {
|
||||
|
@ -91,29 +97,41 @@ class SqlEditor extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
this.onResize();
|
||||
window.addEventListener('resize', this.throttledResize);
|
||||
// We need to measure the height of the sql editor post render to figure the height of
|
||||
// the south pane so it gets rendered properly
|
||||
// eslint-disable-next-line react/no-did-mount-set-state
|
||||
this.setState({ height: this.getSqlEditorHeight() });
|
||||
}
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.throttledResize);
|
||||
onResizeStart() {
|
||||
// Set the heights on the ace editor and the ace content area after drag starts
|
||||
// to smooth out the visual transition to the new heights when drag ends
|
||||
document.getElementById('brace-editor').style.height = `calc(100% - ${SQL_TOOLBAR_HEIGHT}px)`;
|
||||
document.getElementsByClassName('ace_content')[0].style.height = '100%';
|
||||
}
|
||||
onResize() {
|
||||
const height = this.sqlEditorHeight();
|
||||
const editorPaneHeight = this.props.queryEditor.height || 200;
|
||||
const splitPaneHandlerHeight = 8; // 4px of height + 4px of top-margin
|
||||
this.setState({
|
||||
editorPaneHeight,
|
||||
southPaneHeight: height - editorPaneHeight - splitPaneHandlerHeight,
|
||||
height,
|
||||
});
|
||||
onResizeEnd([northPercent, southPercent]) {
|
||||
this.setState(this.getAceEditorAndSouthPaneHeights(
|
||||
this.state.height, northPercent, southPercent));
|
||||
|
||||
if (this.refs.ace && this.refs.ace.clientHeight) {
|
||||
this.props.actions.persistEditorHeight(this.props.queryEditor, this.refs.ace.clientHeight);
|
||||
if (this.northPaneRef.current && this.northPaneRef.current.clientHeight) {
|
||||
this.props.actions.persistEditorHeight(this.props.queryEditor,
|
||||
this.northPaneRef.current.clientHeight);
|
||||
}
|
||||
}
|
||||
onSqlChanged(sql) {
|
||||
this.setState({ sql });
|
||||
}
|
||||
// One layer of abstraction for easy spying in unit tests
|
||||
getSqlEditorHeight() {
|
||||
return this.sqlEditorRef.current.clientHeight;
|
||||
}
|
||||
// Return the heights for the ace editor and the south pane as an object
|
||||
// given the height of the sql editor, north pane percent and south pane percent.
|
||||
getAceEditorAndSouthPaneHeights(height, northPercent, southPercent) {
|
||||
return {
|
||||
aceEditorHeight: height * northPercent / 100 - SQL_TOOLBAR_HEIGHT - GUTTER_HEIGHT / 2,
|
||||
southPaneHeight: height * southPercent / 100,
|
||||
};
|
||||
}
|
||||
getHotkeyConfig() {
|
||||
return [
|
||||
{
|
||||
|
@ -187,9 +205,42 @@ class SqlEditor extends React.PureComponent {
|
|||
ctasChanged(event) {
|
||||
this.setState({ ctas: event.target.value });
|
||||
}
|
||||
sqlEditorHeight() {
|
||||
const horizontalScrollbarHeight = 25;
|
||||
return parseInt(this.props.getHeight(), 10) - horizontalScrollbarHeight;
|
||||
queryPane() {
|
||||
const hotkeys = this.getHotkeyConfig();
|
||||
const { aceEditorHeight, southPaneHeight } = this.getAceEditorAndSouthPaneHeights(
|
||||
this.state.height, INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT);
|
||||
return (
|
||||
<div className="queryPane">
|
||||
<Split
|
||||
sizes={[INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT]}
|
||||
minSize={200}
|
||||
direction="vertical"
|
||||
gutterSize={GUTTER_HEIGHT}
|
||||
onDragStart={this.onResizeStart}
|
||||
onDragEnd={this.onResizeEnd}
|
||||
>
|
||||
<div ref={this.northPaneRef}>
|
||||
<AceEditorWrapper
|
||||
actions={this.props.actions}
|
||||
onBlur={this.setQueryEditorSql}
|
||||
onChange={this.onSqlChanged}
|
||||
queryEditor={this.props.queryEditor}
|
||||
sql={this.props.queryEditor.sql}
|
||||
tables={this.props.tables}
|
||||
height={`${this.state.aceEditorHeight || aceEditorHeight}px`}
|
||||
hotkeys={hotkeys}
|
||||
/>
|
||||
{this.renderEditorBottomBar(hotkeys)}
|
||||
</div>
|
||||
<SouthPane
|
||||
editorQueries={this.props.editorQueries}
|
||||
dataPreviewQueries={this.props.dataPreviewQueries}
|
||||
actions={this.props.actions}
|
||||
height={this.state.southPaneHeight || southPaneHeight}
|
||||
/>
|
||||
</Split>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
renderEditorBottomBar(hotkeys) {
|
||||
let ctasControls;
|
||||
|
@ -305,74 +356,23 @@ class SqlEditor extends React.PureComponent {
|
|||
);
|
||||
}
|
||||
render() {
|
||||
const height = this.sqlEditorHeight();
|
||||
const defaultNorthHeight = this.props.queryEditor.height || 200;
|
||||
const hotkeys = this.getHotkeyConfig();
|
||||
return (
|
||||
<div
|
||||
className="SqlEditor"
|
||||
style={{
|
||||
height: height + 'px',
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Collapse
|
||||
in={!this.props.hideLeftBar}
|
||||
>
|
||||
<Col
|
||||
xs={6}
|
||||
sm={5}
|
||||
md={4}
|
||||
lg={3}
|
||||
>
|
||||
<SqlEditorLeftBar
|
||||
height={height}
|
||||
database={this.props.database}
|
||||
queryEditor={this.props.queryEditor}
|
||||
tables={this.props.tables}
|
||||
actions={this.props.actions}
|
||||
/>
|
||||
</Col>
|
||||
</Collapse>
|
||||
<Col
|
||||
xs={this.props.hideLeftBar ? 12 : 6}
|
||||
sm={this.props.hideLeftBar ? 12 : 7}
|
||||
md={this.props.hideLeftBar ? 12 : 8}
|
||||
lg={this.props.hideLeftBar ? 12 : 9}
|
||||
style={{ height: this.state.height }}
|
||||
>
|
||||
<SplitPane
|
||||
split="horizontal"
|
||||
defaultSize={defaultNorthHeight}
|
||||
minSize={100}
|
||||
onChange={this.onResize}
|
||||
>
|
||||
<div ref="ace" style={{ width: '100%' }}>
|
||||
<div>
|
||||
<AceEditorWrapper
|
||||
actions={this.props.actions}
|
||||
onBlur={this.setQueryEditorSql}
|
||||
onChange={this.onSqlChanged}
|
||||
queryEditor={this.props.queryEditor}
|
||||
sql={this.props.queryEditor.sql}
|
||||
tables={this.props.tables}
|
||||
height={((this.state.editorPaneHeight || defaultNorthHeight) - 50) + 'px'}
|
||||
hotkeys={hotkeys}
|
||||
/>
|
||||
{this.renderEditorBottomBar(hotkeys)}
|
||||
</div>
|
||||
</div>
|
||||
<div ref="south">
|
||||
<SouthPane
|
||||
editorQueries={this.props.editorQueries}
|
||||
dataPreviewQueries={this.props.dataPreviewQueries}
|
||||
actions={this.props.actions}
|
||||
height={this.state.southPaneHeight || 0}
|
||||
/>
|
||||
</div>
|
||||
</SplitPane>
|
||||
</Col>
|
||||
</Row>
|
||||
<div ref={this.sqlEditorRef} className="SqlEditor">
|
||||
<CSSTransition
|
||||
classNames="schemaPane"
|
||||
in={!this.props.hideLeftBar}
|
||||
timeout={300}
|
||||
>
|
||||
<div className="schemaPane">
|
||||
<SqlEditorLeftBar
|
||||
database={this.props.database}
|
||||
queryEditor={this.props.queryEditor}
|
||||
tables={this.props.tables}
|
||||
actions={this.props.actions}
|
||||
/>
|
||||
</div>
|
||||
</CSSTransition>
|
||||
{this.queryPane()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { t } from '@superset-ui/translation';
|
||||
|
||||
import TableElement from './TableElement';
|
||||
import TableSelector from '../../components/TableSelector';
|
||||
|
||||
|
@ -106,7 +105,7 @@ export default class SqlEditorLeftBar extends React.PureComponent {
|
|||
const tableMetaDataHeight = this.props.height - 130; // 130 is the height of the selects above
|
||||
const qe = this.props.queryEditor;
|
||||
return (
|
||||
<div className="clearfix">
|
||||
<div className="sqlEditorLeftBar">
|
||||
<TableSelector
|
||||
dbId={qe.dbId}
|
||||
schema={qe.schema}
|
||||
|
|
|
@ -39,7 +39,6 @@ const propTypes = {
|
|||
queryEditors: PropTypes.array,
|
||||
tabHistory: PropTypes.array.isRequired,
|
||||
tables: PropTypes.array.isRequired,
|
||||
getHeight: PropTypes.func.isRequired,
|
||||
offline: PropTypes.bool,
|
||||
};
|
||||
const defaultProps = {
|
||||
|
@ -238,7 +237,6 @@ class TabbedSqlEditors extends React.PureComponent {
|
|||
<div className="panel-body">
|
||||
{isSelected && (
|
||||
<SqlEditor
|
||||
getHeight={this.props.getHeight}
|
||||
tables={this.props.tables.filter(xt => xt.queryEditorId === qe.id)}
|
||||
queryEditor={qe}
|
||||
editorQueries={this.state.queriesArray}
|
||||
|
|
|
@ -135,7 +135,8 @@ div.Workspace {
|
|||
background-color: #e8e8e8;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 2px solid #ccc;
|
||||
border: 1px solid #ccc;
|
||||
border-top: 0;
|
||||
|
||||
form {
|
||||
margin-block-end: 0;
|
||||
|
@ -193,21 +194,67 @@ div.Workspace {
|
|||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.SqlEditor {
|
||||
.Resizer {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
.SqlLab {
|
||||
.tab-content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.Resizer.horizontal {
|
||||
height: 4px;
|
||||
#brace-editor {
|
||||
height: calc(100% - 51px);
|
||||
}
|
||||
|
||||
.ace_content {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.SouthPane {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.SqlEditor {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
|
||||
.schemaPane {
|
||||
flex-grow: 1;
|
||||
transition: all .3s ease-in-out;
|
||||
}
|
||||
|
||||
.schemaPane-enter-done, .schemaPane-exit {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.schemaPane-enter-active, .schemaPane-exit-active {
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.schemaPane-enter, .schemaPane-exit-done {
|
||||
transform: translateX(-100%);
|
||||
max-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.queryPane {
|
||||
flex-grow: 8;
|
||||
position: relative;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.schemaPane-exit-done + .queryPane {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.gutter {
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 3%;
|
||||
margin: 3px 47%;
|
||||
}
|
||||
|
||||
.gutter.gutter-vertical {
|
||||
cursor: row-resize;
|
||||
width: 4%;
|
||||
margin-top: 4px;
|
||||
margin-left: 47%;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,9 +345,6 @@ a.Link {
|
|||
.tooltip-inner {
|
||||
max-width: 500px;
|
||||
}
|
||||
.SplitPane.horizontal {
|
||||
padding-right: 4px;
|
||||
}
|
||||
.SouthPane {
|
||||
margin-top: 10px;
|
||||
position: absolute;
|
||||
|
|
|
@ -83,17 +83,15 @@ class AsyncSelect extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Select
|
||||
placeholder={this.props.placeholder}
|
||||
options={this.state.options}
|
||||
value={this.props.value}
|
||||
isLoading={this.state.isLoading}
|
||||
onChange={this.onChange}
|
||||
valueRenderer={this.props.valueRenderer}
|
||||
{...this.props}
|
||||
/>
|
||||
</div>
|
||||
<Select
|
||||
placeholder={this.props.placeholder}
|
||||
options={this.state.options}
|
||||
value={this.props.value}
|
||||
isLoading={this.state.isLoading}
|
||||
onChange={this.onChange}
|
||||
valueRenderer={this.props.valueRenderer}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,22 @@
|
|||
* under the License.
|
||||
*/
|
||||
.TableSelector .fa-refresh {
|
||||
padding-top: 7px
|
||||
padding-left: 9px;
|
||||
}
|
||||
.TableSelector .refresh-col {
|
||||
padding-left: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
}
|
||||
.TableSelector .section {
|
||||
padding-bottom: 5px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.TableSelector .select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.TableSelector .divider {
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import Select from 'react-virtualized-select';
|
||||
import createFilterOptions from 'react-select-fast-filter-options';
|
||||
import { ControlLabel, Col, Label, Row } from 'react-bootstrap';
|
||||
import { ControlLabel, Label } from 'react-bootstrap';
|
||||
import { t } from '@superset-ui/translation';
|
||||
import { SupersetClient } from '@superset-ui/connection';
|
||||
|
||||
|
@ -38,7 +38,6 @@ const propTypes = {
|
|||
tableNameSticky: PropTypes.bool,
|
||||
tableName: PropTypes.string,
|
||||
database: PropTypes.object,
|
||||
horizontal: PropTypes.bool,
|
||||
sqlLabMode: PropTypes.bool,
|
||||
onChange: PropTypes.func,
|
||||
clearable: PropTypes.bool,
|
||||
|
@ -52,7 +51,6 @@ const defaultProps = {
|
|||
onTableChange: () => {},
|
||||
onChange: () => {},
|
||||
tableNameSticky: true,
|
||||
horizontal: false,
|
||||
sqlLabMode: true,
|
||||
clearable: true,
|
||||
};
|
||||
|
@ -199,10 +197,10 @@ export default class TableSelector extends React.PureComponent {
|
|||
}
|
||||
renderSelectRow(select, refreshBtn) {
|
||||
return (
|
||||
<Row>
|
||||
<Col md={11}>{select}</Col>
|
||||
<Col md={1} className="refresh-col">{refreshBtn}</Col>
|
||||
</Row>
|
||||
<div className="section">
|
||||
<span className="select">{select}</span>
|
||||
<span className="refresh-col">{refreshBtn}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
renderDatabaseSelect() {
|
||||
|
@ -232,29 +230,25 @@ export default class TableSelector extends React.PureComponent {
|
|||
/>);
|
||||
}
|
||||
renderSchema() {
|
||||
return (
|
||||
<div className="m-t-5">
|
||||
{this.renderSelectRow(
|
||||
<Select
|
||||
name="select-schema"
|
||||
placeholder={t('Select a schema (%s)', this.state.schemaOptions.length)}
|
||||
options={this.state.schemaOptions}
|
||||
value={this.props.schema}
|
||||
valueRenderer={o => (
|
||||
<div>
|
||||
<span className="text-muted">{t('Schema:')}</span> {o.label}
|
||||
</div>
|
||||
)}
|
||||
isLoading={this.state.schemaLoading}
|
||||
autosize={false}
|
||||
onChange={this.changeSchema}
|
||||
/>,
|
||||
<RefreshLabel
|
||||
onClick={() => this.onDatabaseChange({ id: this.props.dbId }, true)}
|
||||
tooltipContent={t('Force refresh schema list')}
|
||||
/>,
|
||||
return this.renderSelectRow(
|
||||
<Select
|
||||
name="select-schema"
|
||||
placeholder={t('Select a schema (%s)', this.state.schemaOptions.length)}
|
||||
options={this.state.schemaOptions}
|
||||
value={this.props.schema}
|
||||
valueRenderer={o => (
|
||||
<div>
|
||||
<span className="text-muted">{t('Schema:')}</span> {o.label}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
isLoading={this.state.schemaLoading}
|
||||
autosize={false}
|
||||
onChange={this.changeSchema}
|
||||
/>,
|
||||
<RefreshLabel
|
||||
onClick={() => this.onDatabaseChange({ id: this.props.dbId }, true)}
|
||||
tooltipContent={t('Force refresh schema list')}
|
||||
/>,
|
||||
);
|
||||
}
|
||||
renderTable() {
|
||||
|
@ -290,20 +284,16 @@ export default class TableSelector extends React.PureComponent {
|
|||
value={this.state.tableName}
|
||||
loadOptions={this.getTableNamesBySubStr}
|
||||
/>);
|
||||
return (
|
||||
<div className="m-t-5">
|
||||
{this.renderSelectRow(
|
||||
select,
|
||||
<RefreshLabel
|
||||
onClick={() => this.changeSchema({ value: this.props.schema }, true)}
|
||||
tooltipContent={t('Force refresh table list')}
|
||||
/>)}
|
||||
</div>);
|
||||
return this.renderSelectRow(
|
||||
select,
|
||||
<RefreshLabel
|
||||
onClick={() => this.changeSchema({ value: this.props.schema }, true)}
|
||||
tooltipContent={t('Force refresh table list')}
|
||||
/>);
|
||||
}
|
||||
renderSeeTableLabel() {
|
||||
return (
|
||||
<div>
|
||||
<hr />
|
||||
<div className="section">
|
||||
<ControlLabel>
|
||||
{t('See table schema')}{' '}
|
||||
<small>
|
||||
|
@ -319,18 +309,11 @@ export default class TableSelector extends React.PureComponent {
|
|||
render() {
|
||||
return (
|
||||
<div className="TableSelector">
|
||||
{this.props.horizontal ?
|
||||
<div>
|
||||
<Col md={4}>{this.renderDatabaseSelect()}</Col>
|
||||
<Col md={4}>{this.renderSchema()}</Col>
|
||||
<Col md={4}>{this.renderTable()}</Col>
|
||||
</div> :
|
||||
<div>
|
||||
<div>{this.renderDatabaseSelect()}</div>
|
||||
<div className="m-t-5">{this.renderSchema()}</div>
|
||||
{this.props.sqlLabMode && this.renderSeeTableLabel()}
|
||||
<div className="m-t-5">{this.renderTable()}</div>
|
||||
</div>}
|
||||
{this.renderDatabaseSelect()}
|
||||
{this.renderSchema()}
|
||||
<div className="divider" />
|
||||
{this.props.sqlLabMode && this.renderSeeTableLabel()}
|
||||
{this.renderTable()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue