sqlpad
Version:
Web app for writing and running SQL queries and visualizing the results. Supports Postgres, MySQL, SQL Server, Crate and Vertica.
160 lines (148 loc) • 4.84 kB
JavaScript
import React from 'react'
import { Table, Column, Cell } from 'fixed-data-table-2'
import SpinKitCube from './SpinKitCube.js'
import moment from 'moment'
import '../css/fixed-data-table.css'
import './QueryResultDataTable.css'
const renderValue = (input, fieldMeta) => {
if (input === null || input === undefined) {
return <em>null</em>
} else if (input === true || input === false) {
return input.toString()
} else if (fieldMeta.datatype === 'date') {
return moment.utc(input).format('MM/DD/YYYY HH:mm:ss')
} else if (typeof input === 'object') {
return JSON.stringify(input, null, 2)
} else {
return input
}
}
// NOTE: PureComponent's shallow compare works for this component
// because the isRunning prop will toggle with each query execution
// It would otherwise not rerender on change of prop.queryResult alone
class QueryResultDataTable extends React.PureComponent {
state = {
gridWidth: 0,
gridHeight: 0,
columnWidths: {}
}
handleResize = e => {
const resultGrid = document.getElementById('result-grid')
if (resultGrid) {
this.setState({
gridHeight: resultGrid.clientHeight,
gridWidth: resultGrid.clientWidth
})
}
}
handleColumnResizeEnd = (newColumnWidth, columnKey) => {
this.setState(({ columnWidths }) => ({
columnWidths: {
...columnWidths,
[columnKey]: newColumnWidth
}
}))
}
componentDidMount() {
window.addEventListener('resize', this.handleResize)
this.handleResize()
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize)
}
render() {
if (this.props.isRunning) {
return (
<div id="result-grid" className="result-grid run-result-notification">
<SpinKitCube />
</div>
)
} else if (this.props.queryError) {
return (
<div
id="result-grid"
className="result-grid run-result-notification label-danger"
>
{this.props.queryError}
</div>
)
} else if (this.props.queryResult && this.props.queryResult.rows) {
const { columnWidths } = this.state
const queryResult = this.props.queryResult
const columnNodes = queryResult.fields.map(function(field) {
const fieldMeta = queryResult.meta[field]
let valueLength = fieldMeta.maxValueLength
if (field.length > valueLength) {
valueLength = field.length
}
let columnWidthGuess = valueLength * 20
if (columnWidthGuess < 200) {
columnWidthGuess = 200
} else if (columnWidthGuess > 350) {
columnWidthGuess = 350
}
const columnWidth = columnWidths[field] || columnWidthGuess
return (
<Column
columnKey={field}
key={field}
isResizable
header={<Cell>{field}</Cell>}
cell={({ rowIndex }) => {
const value = queryResult.rows[rowIndex][field]
let barStyle
let numberBar
if (fieldMeta.datatype === 'number') {
const valueNumber = Number(value)
const range =
fieldMeta.max - (fieldMeta.min < 0 ? fieldMeta.min : 0)
let left = 0
if (fieldMeta.min < 0 && valueNumber < 0) {
left =
Math.abs(fieldMeta.min - valueNumber) / range * 100 + '%'
} else if (fieldMeta.min < 0 && valueNumber >= 0) {
left = Math.abs(fieldMeta.min) / range * 100 + '%'
}
barStyle = {
position: 'absolute',
left: left,
bottom: 0,
height: '2px',
width: Math.abs(valueNumber) / range * 100 + '%',
backgroundColor: '#555'
}
numberBar = <div style={barStyle} />
}
return (
<Cell>
{numberBar}
<div style={{ whiteSpace: 'nowrap' }}>
{renderValue(value, fieldMeta)}
</div>
</Cell>
)
}}
width={columnWidth}
/>
)
})
return (
<div id="result-grid" className="result-grid">
<Table
rowHeight={30}
rowsCount={queryResult.rows.length}
width={this.state.gridWidth}
height={this.state.gridHeight}
headerHeight={30}
onColumnResizeEndCallback={this.handleColumnResizeEnd}
>
{columnNodes}
</Table>
</div>
)
} else {
return <div id="result-grid" className="result-grid" />
}
}
}
export default QueryResultDataTable