Skip to content

Commit

Permalink
Pretty good state
Browse files Browse the repository at this point in the history
  • Loading branch information
mistercrunch committed Jul 13, 2016
1 parent 32abf9b commit 7abd6f8
Show file tree
Hide file tree
Showing 14 changed files with 386 additions and 126 deletions.
1 change: 0 additions & 1 deletion caravel/assets/.eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@
"no-with": [2],
"no-whitespace-before-property": [2],
"object-curly-spacing": [2, "always"],
"object-shorthand": [2, "never"],
"one-var": [0],
"one-var-declaration-per-line": [2, "initializations"],
"operator-assignment": [0, "always"],
Expand Down
9 changes: 9 additions & 0 deletions caravel/assets/javascripts/SqlAnvil/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# TODO
* Run
* Sync with db
* SqlAlchemy Editor? Security?

# Questions
* Ok to use objects in ADD_TABLE?
* Ok to mix state / redux?
* Ok to keep async out of redux?
41 changes: 35 additions & 6 deletions caravel/assets/javascripts/SqlAnvil/actions.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,39 @@
export const ADD_QUERY = 'ADD_QUERY'
export const ADD_TABLE = 'ADD_TABLE'
export const ADD_QUERY_EDITOR = 'ADD_QUERY_EDITOR';
export const REMOVE_QUERY_EDITOR = 'REMOVE_QUERY_EDITOR';
export const ADD_TABLE = 'ADD_TABLE';
export const REMOVE_TABLE = 'REMOVE_TABLE';
export const HIDE_TABLE_POPUP = 'HIDE_TABLE_POPUP';
export const SHOW_TABLE_POPUP = 'SHOW_TABLE_POPUP';
export const START_QUERY = 'START_QUERY';
export const END_QUERY = 'END_QUERY';

export function addQuery(title, sql) {
return { type: ADD_QUERY, title, sql }
export function addQueryEditor(id, title, sql) {
return { type: ADD_QUERY_EDITOR, id, title, sql };
}

export function addTable(dbId, tableName) {
return { type: ADD_TABLE, dbId, tableName }
export function removeQueryEditor(id) {
return { type: REMOVE_QUERY_EDITOR, id };
}

export function addTable(id, dbId, name, columns = [], showPopup = true) {
return {
type: ADD_TABLE,
id, dbId, name, showPopup, columns
};
}

export function hideTablePopup(id) {
return { type: HIDE_TABLE_POPUP, id };
}

export function showTablePopup(id) {
return { type: SHOW_TABLE_POPUP, id };
}

export function removeTable(id) {
return { type: REMOVE_TABLE, id };
}

export function startQuery(id, sqlEditorId, sql, startDttm) {
return { type: START_QUERY, id, sqlEditorId, sql, startDttm };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React, { PropTypes } from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { Button } from 'react-bootstrap'


const ButtonWithTooltip = React.createClass({
render() {
let tooltip = (
<Tooltip id="tooltip">
{this.props.tooltip}
</Tooltip>
);
return (
<OverlayTrigger overlay={tooltip} delayShow={300} delayHide={150}>
<Button
onClick={this.props.onClick}
className={this.props.className}>
{this.props.children}
</Button>
</OverlayTrigger>
);
}
});

export default ButtonWithTooltip
7 changes: 5 additions & 2 deletions caravel/assets/javascripts/SqlAnvil/components/Link.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ const Link = React.createClass({
);
return (
<OverlayTrigger overlay={tooltip} delayShow={300} delayHide={150}>
<a href={this.props.href} className={this.props.className}>
{this.props.children}
<a
href={this.props.href}
onClick={this.props.onClick}
className={this.props.className}>
{this.props.children}
</a>
</OverlayTrigger>
);
Expand Down
40 changes: 29 additions & 11 deletions caravel/assets/javascripts/SqlAnvil/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,58 @@ import 'brace/mode/sql';
import 'brace/theme/chrome';

import { ResultSet } from './Mocks'
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions';
import moment from 'moment'

const SqlEditor = React.createClass({
getInitialState: function() {
return {
collapsed: false,
startQueryDttm: null,
tables: [],
sql: "SELECT *\nFROM \nWHERE ",
sql: this.props.queryEditor.sql,
};
},
toggleCollapse: function () {
this.setState({ collapsed: !this.state.collapsed });
this.render();
},
stopwatch: function () {
this.setState({ clockStr: new Date() - this.startQueryDttm });
var duration = moment().valueOf() - this.state.startQueryDttm.valueOf();
console.log(duration);
duration = moment.utc(duration);

this.setState({ clockStr: duration.format('HH:mm:ss') });
this.render();
},
startQuery: function () {
this.setState({ startQueryDttm: new Date() });
this.timer = setInterval(this.stopwatch, 100);
this.setState({ startQueryDttm: moment() });
this.timer = setInterval(this.stopwatch, 500);
this.render();
},
textChange: function (text) {
this.setState({ sql: text });
},
render: function () {
var body = (<div/>);
var results = this.state.startQueryDttm ? <img className="loading" src="/static/assets/images/loading.gif"/> : <ResultSet/>;
if (!this.state.collapsed) {
body = (
<div>
<AceEditor
mode="sql"
name={this.props.name}
theme="chrome"
minLines={3}
minLines={10}
maxLines={30}
onChange={this.textChange}
height="200px"
width="100%"
editorProps={{$blockScrolling: true}}
value={this.state.sql}/>
<ResultSet/>
{results}
</div>
);
}
Expand All @@ -59,9 +68,7 @@ const SqlEditor = React.createClass({
);
if (this.state.startQueryDttm) {
runButton = (
<Button className="clock">
{new Date() - this.state.startQueryDttm}
</Button>
<Button className="clock">{this.state.clockStr}</Button>
);
}
var rightButtons = (
Expand All @@ -72,7 +79,12 @@ const SqlEditor = React.createClass({
<a className={(this.state.collapsed) ? 'fa fa-angle-down' : 'fa fa-angle-up'}/>
</Button>
<Button bsSize="small"><a className="fa fa-cog"/></Button>
<Button bsSize="small"><a className="fa fa-close"/></Button>
<Button bsSize="small"><a className="fa fa-save"/></Button>
<Button bsSize="small">
<a
className="fa fa-close"
onClick={this.props.actions.removeQueryEditor.bind(this, this.props.queryEditor.id)}/>
</Button>
</ButtonGroup>
);
return (
Expand All @@ -99,4 +111,10 @@ const SqlEditor = React.createClass({
}
});

export default SqlEditor
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
};
}

export default connect(null, mapDispatchToProps)(SqlEditor)
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from 'react'
import { Button, Tab, Tabs } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import SqlEditor from './SqlEditor'
import shortid from 'shortid'

var queryCount = 1;

const QueryEditors = React.createClass({
getInitialState: function() {
return {tabkey: 0};
},
newQueryEditor: function () {
var id = shortid.generate();
queryCount++;
this.props.actions.addQueryEditor(id, `Query ${queryCount}`, "SELECT...");
},
handleSelect(key) {
this.setState({tabkey: key});
},
render: function () {
var editors = this.props.queryEditors.map(function (qe, i) {
return (
<Tab
key={qe.id}
title={qe.title}
eventKey={i}>
<SqlEditor name={qe.id} queryEditor={qe}/>
</Tab>);
});
return (
<Tabs activeKey={this.state.tabkey} onSelect={this.handleSelect}>
{editors}
<Tab title="+" eventKey={this.props.queryEditors.length}>
<Button onClick={this.newQueryEditor}>
Add Tab
</Button>
</Tab>
</Tabs>
);
}
});

function mapStateToProps(state) {
return {
queryEditors: state.queryEditors
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
};
}

export default connect(mapStateToProps, mapDispatchToProps)(QueryEditors)

66 changes: 31 additions & 35 deletions caravel/assets/javascripts/SqlAnvil/components/TableOverlay.jsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,42 @@
import React from 'react';
import Draggable from 'react-draggable';
import { Table } from 'reactable';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';

const TableOverlay = React.createClass({
getInitialState() {
return {
visible: this.props.visible,
};
},
close() {
this.setState({ visible: false });
this.props.closeCallback();
this.render();
},
render() {
if (this.state.visible) {
return <Draggable
defaultPosition={this.props.defaultPosition}
position={null}
handle=".handle"
zIndex={500}>
<div className="window panel panel-default">
<div className="panel-heading handle">
<strong>{this.props.data.name}</strong>
<div className="pull-right">
<a onClick={this.close} href="#"><i className="fa fa-close"/></a>
</div>
</div>
<div className="panel-body nopadding">
<Table
className="table table-condensed table-striped small table-bordered"
sortable={true}
columns={['name', 'type']}
data={this.props.data.columns}/>
return <Draggable
defaultPosition={this.props.defaultPosition}
position={null}
handle=".handle"
zIndex={500}>
<div className="window panel panel-default">
<div className="panel-heading handle">
<strong>{this.props.table.name}</strong>
<div className="pull-right">
<a onClick={this.props.actions.hideTablePopup.bind(this, this.props.table.id) } href="#">
<i className="fa fa-close"/>
</a>
</div>
</div>
</Draggable>;
}
else {
return <div/>;
}
<div className="panel-body nopadding">
<Table
className="table table-condensed table-striped small table-bordered"
sortable={true}
columns={['name', 'type']}
data={this.props.table.columns}/>
</div>
</div>
</Draggable>;
}
});

export default TableOverlay
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch)
};
}

export default connect(null, mapDispatchToProps)(TableOverlay)
Loading

0 comments on commit 7abd6f8

Please sign in to comment.