diff --git a/src/firefly/js/tables/TableConnector.js b/src/firefly/js/tables/TableConnector.js index b2dd881900..3b7584b05d 100644 --- a/src/firefly/js/tables/TableConnector.js +++ b/src/firefly/js/tables/TableConnector.js @@ -87,7 +87,12 @@ export class TableConnector { const {hlRowIdx, startIdx} = TblUtil.getTblInfoById(this.tbl_id); if (rowIdx !== hlRowIdx) { const highlightedRow = startIdx + rowIdx; - TblCntlr.dispatchTableHighlight(this.tbl_id, highlightedRow); + if (this.origTableModel) { + const tableModel = {tbl_id: this.tbl_id, highlightedRow}; + flux.process({type: TblCntlr.TABLE_UPDATE, payload: tableModel}); + } else { + TblCntlr.dispatchTableHighlight(this.tbl_id, highlightedRow); + } } } diff --git a/src/firefly/js/tables/TableUtil.js b/src/firefly/js/tables/TableUtil.js index 7f3616f07c..5a37855bcb 100644 --- a/src/firefly/js/tables/TableUtil.js +++ b/src/firefly/js/tables/TableUtil.js @@ -441,7 +441,7 @@ export function getTblInfo(tableModel, aPageSize) { const currentPage = highlightedRow >= 0 ? Math.floor(highlightedRow / pageSize)+1 : 1; const hlRowIdx = highlightedRow >= 0 ? highlightedRow % pageSize : 0; const startIdx = (currentPage-1) * pageSize; - const endIdx = Math.min(startIdx+pageSize, totalRows) || startIdx ; + const endIdx = Math.min(startIdx+pageSize, totalRows) || get(tableModel,'tableData.data.length', startIdx) ; var totalPages = Math.ceil((totalRows || 0)/pageSize); return { tableModel, tbl_id, title, totalRows, request, startIdx, endIdx, hlRowIdx, currentPage, pageSize,totalPages, highlightedRow, selectInfo, error}; } diff --git a/src/firefly/js/tables/ui/TableRenderer.js b/src/firefly/js/tables/ui/TableRenderer.js index 0fbdd94059..4d6434602f 100644 --- a/src/firefly/js/tables/ui/TableRenderer.js +++ b/src/firefly/js/tables/ui/TableRenderer.js @@ -177,12 +177,11 @@ export class TextCell extends React.Component { // } // render() { - const lineHeight = this.props.height - 6 + 'px'; // 6 is the top/bottom padding. - const style = Object.assign({lineHeight}, this.props.style || {}); var val = getValue(this.props); - val = (isString(val) && val.search(html_regex) >= 0) ?
: val; + const lineHeight = this.props.height - 6 + 'px'; // 6 is the top/bottom padding. + val = (val.search && val.search(html_regex) >= 0) ?
: val; return ( -
{val}
+
{val}
); } } diff --git a/src/firefly/js/ui/TextButton.jsx b/src/firefly/js/ui/TextButton.jsx new file mode 100644 index 0000000000..3983d1f3a4 --- /dev/null +++ b/src/firefly/js/ui/TextButton.jsx @@ -0,0 +1,51 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ + + +import React, {PropTypes} from 'react'; + +const labelStyle= { + display: 'inline-block', + lineHeight: '30px', + fontSize: '10pt', + fontStyle: 'italic', + color: 'blue', + verticalAlign:'baseline' +}; + +/** + * + * @param text + * @param tip + * @param onClick + * @param style + * @return react object + */ +export function TextButton({text, tip='$text',style={}, onClick}) { + var s= Object.assign({cursor:'pointer', verticalAlign:'bottom'},style); + return ( +
+ +
+ {text} +
+ +
+ ); +} + + +//CloseButton.contextTypes= { +//}; + + +TextButton.propTypes= { + text : PropTypes.string, + style : PropTypes.object, + tip : PropTypes.string, + onClick : PropTypes.func +}; + + + diff --git a/src/firefly/js/visualize/ColSelectView.jsx b/src/firefly/js/visualize/ColSelectView.jsx new file mode 100644 index 0000000000..996b308804 --- /dev/null +++ b/src/firefly/js/visualize/ColSelectView.jsx @@ -0,0 +1,139 @@ +/* + */ +import React from 'react'; +import DialogRootContainer from '../ui/DialogRootContainer.jsx'; +import {PopupPanel} from '../ui/PopupPanel.jsx'; +import {dispatchTableRemove} from '../tables/TablesCntlr'; + +import {BasicTable} from '../tables/ui/BasicTable.jsx'; +import {getTblById} from '../tables/TableUtil.js'; +import {dispatchShowDialog} from '../core/ComponentCntlr.js'; +import CompleteButton from '../ui/CompleteButton.jsx'; +import HelpIcon from '../ui/HelpIcon.jsx'; +const popupId = 'XYColSelect'; +const TBL_ID ='selectCol'; + +const popupPanelResizableStyle = { + width: 600, + minWidth: 450, + height: 450, + minHeight: 300, + resize: 'both', + overflow: 'hidden', + position: 'relative' +}; + + +//define the table style only in the table div +const tableStyle = {boxSizing: 'border-box', paddingLeft:5,paddingRight:5, width: '100%', height: 'calc(100% - 70px)', overflow: 'hidden', flexGrow: 1, display: 'flex', resize:'none'}; + +//define the complete button +const closeButtonStyle = {'textAlign': 'center', display: 'inline-block', height:40, marginTop:10, width: '90%'}; +//define the helpButton +const helpIdStyle = {'textAlign': 'center', display: 'inline-block', height:40, marginRight: 20}; + + +export function showColSelectPopup(colValStats,onColSelected,popupTitle,buttonText,currentVal) { + + if (getTblById(TBL_ID)) { +        dispatchTableRemove(TBL_ID); +    } + + const colNames = colValStats.map((colVal) => {return colVal.name;}); + var hlRowNum = getHlRow(currentVal,colNames) || 0; + + // make a local table for plot column selection panel + var columns = [ + {name: 'Name',visibility: 'show', prefWidth: 12}, + {name: 'Unit',visibility: 'show', prefWidth: 8}, + {name: 'Type',visibility: 'show', prefWidth: 8}, + {name: 'Description',visibility: 'show', prefWidth: 60} + ]; + var data = []; + for (var i = 0; i < colValStats.length; i++) { + data[i] = [ + colValStats[i].name, + colValStats[i].unit, + colValStats[i].type, + colValStats[i].descr + ]; + } + const request = {pageSize:10000}; + var tableModel = {totalRows: data.length, request, tbl_id:TBL_ID, tableData: {columns, data }, highlightedRow: hlRowNum}; + + + var popup = ( + {popupForm(tableModel,onColSelected,buttonText,popupId)} + + + ); + + DialogRootContainer.defineDialog(popupId, popup); + dispatchShowDialog(popupId); +} + +function popupForm(tableModel, onColSelected,buttonText,popupId) { + const tblId = tableModel.tbl_id; + return ( +
+ { renderTable(tableModel,popupId)} + { renderCloseAndHelpButtons(tblId,onColSelected,buttonText,popupId)} +
+ ); + +} + +/** + * display the data into a tabular format + * @param tableModel + * @param popupId + * @return table section + */ +function renderTable(tableModel,popupId) { + + return ( +
+ +
+ ); + +} + +function renderCloseAndHelpButtons(tblId,onColSelected,buttonText,popupId) { + + return( +
+
+ < CompleteButton + text={buttonText} + onSuccess={()=>setXYColumns(tblId,onColSelected)} + dialogId={popupId} + /> +
+ {/* comment out the help button for now +
+ +
+ */} +
+); +} + +function setXYColumns(tblId,onColSelected) { + const tableModel = getTblById(tblId); + var hlRow = tableModel.highlightedRow || 0; + const selectedColName = tableModel.tableData.data[hlRow][0]; +    onColSelected(selectedColName); + +} + +function getHlRow(currentVal,colNames) { + for(var i = 0; i < colNames.length; i++) { + if (colNames[i] === currentVal) { + return i; + } + } +} \ No newline at end of file diff --git a/src/firefly/js/visualize/ColValuesStatistics.js b/src/firefly/js/visualize/ColValuesStatistics.js index ca66a61814..4b8f6e7401 100644 --- a/src/firefly/js/visualize/ColValuesStatistics.js +++ b/src/firefly/js/visualize/ColValuesStatistics.js @@ -1,10 +1,11 @@ export default class ColValuesStatistics{ - constructor(name, descr, unit, min, max, numpoints) { + constructor(name, descr, unit, min, max, numpoints,type) { this.name = name; this.descr = descr; this.unit = unit; this.min = Number(min); this.max = Number(max); this.numpoints = Number(numpoints); + this.type = type; } }; \ No newline at end of file diff --git a/src/firefly/js/visualize/TableStatsCntlr.js b/src/firefly/js/visualize/TableStatsCntlr.js index 059f6d88da..9d2f35ab17 100644 --- a/src/firefly/js/visualize/TableStatsCntlr.js +++ b/src/firefly/js/visualize/TableStatsCntlr.js @@ -1,6 +1,6 @@ import {flux} from '../Firefly.js'; -import {has, omit} from 'lodash'; +import {get, has, omit} from 'lodash'; import {updateSet, updateMerge} from '../util/WebUtil.js'; import ColValuesStatistics from './ColValuesStatistics.js'; @@ -129,15 +129,23 @@ function fetchTblStats(dispatch, activeTableServerRequest) { const sreq = Object.assign({}, omit(activeTableServerRequest, ['tbl_id', 'META_INFO']), {'startIdx': 0, 'pageSize': 1000000}); - const req = TableUtil.makeTblRequest('StatisticsProcessor', null, - { searchRequest: JSON.stringify(sreq) }, - 'tblstats-'+tbl_id); + const req = TableUtil.makeTblRequest('StatisticsProcessor', `Statistics for ${tbl_id}`, + { searchRequest: JSON.stringify(sreq) }, + {'startIdx': 0, 'pageSize': 1000}); TableUtil.doFetchTable(req).then( (tableModel) => { if (tableModel.tableData && tableModel.tableData.data) { + const columns = get(TableUtil.getTblById(tbl_id), 'tableData.columns', []); const colStats = tableModel.tableData.data.reduce((colstats, arow) => { - colstats.push(new ColValuesStatistics(...arow)); + const r = new ColValuesStatistics(...arow); + const col = columns.find((c)=>{return c.name=== r.name;}); + if (col) { + r.unit = col.units && col.units !== 'null' ? col.units : ''; + r.descr = col.desc ? col.desc: ''; + r.type = col.type ? col.type : ''; + } + colstats.push(r); return colstats; }, []); dispatch(updateTblStats( diff --git a/src/firefly/js/visualize/XYPlotOptions.jsx b/src/firefly/js/visualize/XYPlotOptions.jsx index a9686130de..1ec0672808 100644 --- a/src/firefly/js/visualize/XYPlotOptions.jsx +++ b/src/firefly/js/visualize/XYPlotOptions.jsx @@ -17,10 +17,12 @@ import {RadioGroupInputField} from '../ui/RadioGroupInputField.jsx'; import {SuggestBoxInputField} from '../ui/SuggestBoxInputField.jsx'; import {FieldGroupCollapsible} from '../ui/panel/CollapsiblePanel.jsx'; import {plotParamsShape} from './XYPlot.jsx'; +import {showColSelectPopup} from './ColSelectView.jsx'; const DECI_ENABLE_SIZE = 5000; const helpStyle = {fontStyle: 'italic', color: '#808080', paddingBottom: 10}; +import {TextButton} from '../ui/TextButton.jsx'; /* * Split content into prior content and the last alphanumeric token in the text @@ -116,6 +118,23 @@ export function setOptions(groupKey, xyPlotParams) { dispatchMultiValueChange(groupKey, flds); } +export function getColValidator(colValStats) { + const colNames = colValStats.map((colVal) => {return colVal.name;}); + return (val) => { + let retval = {valid: true, message: ''}; + if (!val) { + return {valid: false, message: 'Can not be empty. Please provide value or expression'}; + } else if (colNames.indexOf(val) < 0) { + const expr = new Expression(val, colNames); + if (!expr.isValid()) { + retval = {valid: false, message: `${expr.getError().error}. Unable to parse ${val}.`}; + } + } + return retval; + }; +} + + var XYPlotOptions = React.createClass({ @@ -126,6 +145,18 @@ var XYPlotOptions = React.createClass({ xyPlotParams: plotParamsShape }, + componentDidMount() { + // make sure column validator matches current columns + const {colValStats, groupKey} = this.props; + if (colValStats) { + const colValidator = getColValidator(colValStats); + const flds = [ + {fieldKey: 'x.columnOrExpr', validator: colValidator}, + {fieldKey: 'y.columnOrExpr', validator: colValidator} + ]; + dispatchMultiValueChange(groupKey, flds); + } + }, shouldComponentUpdate(np) { return this.props.groupKey !== np.groupKey || this.props.colValStats !== np.colValStats || @@ -191,7 +222,6 @@ var XYPlotOptions = React.createClass({ render() { const { colValStats, groupKey, xyPlotParams, onOptionsSelected}= this.props; - const colNames = colValStats.map((colVal) => {return colVal.name;}); // the suggestions are indexes in the colValStats array - it makes it easier to render then with labels const allSuggestions = colValStats.map((colVal,idx)=>{return idx;}); @@ -212,19 +242,27 @@ var XYPlotOptions = React.createClass({ return priorContent+colValStats[idx].name; }; - const colValidator = (val) => { - let retval = {valid: true, message: ''}; - if (!val) { - return {valid: false, message: 'Can not be empty. Please provide value or expression'}; - } else if (colNames.indexOf(val)<0) { - const expr = new Expression(val,colNames); - if (!expr.isValid()) { - retval = {valid: false, message: `${expr.getError().error}. Unable to parse ${val}.`}; - } - } - return retval; - }; - + var x = get(xyPlotParams, 'x.columnOrExpr'); +        var y = get(xyPlotParams, 'y.columnOrExpr'); + const onXColSelected = (colName) => { +            x = colName; + const flds = [ +                {fieldKey: 'x.columnOrExpr', value: colName}, +                {fieldKey: 'x.label', value: ''}, +                {fieldKey: 'x.unit', value: ''} +            ]; +            dispatchMultiValueChange(groupKey, flds); +        }; +        const onYColSelected = (colName) => { + y = colName; +            const flds = [ +                {fieldKey: 'y.columnOrExpr', value: colName}, +                {fieldKey: 'y.label', value: ''}, +                {fieldKey: 'y.unit', value: ''} +            ]; +            dispatchMultiValueChange(groupKey, flds); +        }; +                                          return (

@@ -233,21 +271,42 @@ var XYPlotOptions = React.createClass({ For X and Y, enter a column or an expression
ex. log(col); 100*col1/col2; col1-col2
- + + + + + + + +
+
+ +
+
+
+ showColSelectPopup(colValStats, onXColSelected,'Choose X','Set X',x)} + onSuccess={this.resultsSuccess} + onFail={this.resultsFail} + /> +
+
+ @@ -291,21 +350,42 @@ var XYPlotOptions = React.createClass({
- + + + + + + + +
+
+ +
+
+
+ showColSelectPopup(colValStats,onYColSelected,'Choose Y','Set Y',y)} + onSuccess={this.resultsSuccess} + onFail={this.resultsFail} + /> +
+
+