From a35ef3692ba4ffb11ec169a1ba535261e74ccfe7 Mon Sep 17 00:00:00 2001 From: loi Date: Tue, 9 Feb 2016 17:57:46 -0800 Subject: [PATCH 1/2] DM-4567: Table (JS): large table handling --- .eslintrc | 1 + package.json | 4 +- src/fftools/js/fftools.js | 19 +- .../util/ipactable/DataGroupFilter.java | 14 +- .../util/ipactable/DataGroupReader.java | 2 +- .../util/ipactable/DataGroupWriter.java | 2 +- .../server/util/ipactable/JsonTableUtil.java | 1 - .../edu/caltech/ipac/util/IpacTableUtil.java | 4 +- src/firefly/js/core/AppDataCntlr.js | 4 +- src/firefly/js/core/History.js | 3 +- src/firefly/js/core/ReduxFlux.js | 9 +- src/firefly/js/tables/SelectInfo.js | 4 +- src/firefly/js/tables/Table.js | 30 +- src/firefly/js/tables/TableRequest.js | 7 +- src/firefly/js/tables/TableStore.js | 210 +++++++++++++ src/firefly/js/tables/TableUtil.js | 173 ++++++++--- src/firefly/js/tables/TablesCntlr.js | 99 +++--- src/firefly/js/tables/TablesUiCntlr.js | 82 +++++ .../js/tables/__test__/TablesCntlr-test.js | 4 +- src/firefly/js/tables/reducers/LoadTable.js | 74 ----- src/firefly/js/tables/ui/TablePanel.css | 37 ++- src/firefly/js/tables/ui/TablePanel.jsx | 289 +++++++++--------- src/firefly/js/ui/DropDownToolbarButton.jsx | 4 +- src/firefly/js/ui/FormPanel.jsx | 2 +- src/firefly/js/ui/PointerPopup.jsx | 2 +- src/firefly/js/ui/Toolbar.css | 62 ++++ src/firefly/js/ui/Toolbar.jsx | 37 +++ src/firefly/js/visualize/DrawLayerCntlr.js | 4 +- src/firefly/js/visualize/HistogramCntlr.js | 7 +- src/firefly/js/visualize/PlotViewUtil.js | 3 +- src/firefly/js/visualize/TableStatsCntlr.js | 9 +- src/firefly/js/visualize/XYPlotCntlr.js | 8 +- .../js/visualize/XYPlotTableViewPanel.jsx | 2 +- .../js/visualize/draw/DrawerComponent.jsx | 4 +- .../js/visualize/iv/ImageViewerView.jsx | 2 +- .../js/visualize/reducer/DrawLayerReducer.js | 4 +- .../server/util/ipactable/test_data.json | 2 +- 37 files changed, 820 insertions(+), 404 deletions(-) create mode 100644 src/firefly/js/tables/TableStore.js create mode 100644 src/firefly/js/tables/TablesUiCntlr.js delete mode 100644 src/firefly/js/tables/reducers/LoadTable.js create mode 100644 src/firefly/js/ui/Toolbar.css create mode 100644 src/firefly/js/ui/Toolbar.jsx diff --git a/.eslintrc b/.eslintrc index 5aa944d786..334e2b0916 100644 --- a/.eslintrc +++ b/.eslintrc @@ -13,6 +13,7 @@ "classes" : true, "defaultParams" : true, "destructuring" : true, + "restParams" : true, "forOf": true, "jsx": true, "modules" : true, diff --git a/package.json b/package.json index 0ed1159367..0b76dad88e 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "icepick" : "0.2.0", "isomorphic-fetch": "2.2.1", "local-storage" : "1.4.2", - "lodash" : "3.10.1", + "lodash" : "4.0.1", "numeral" : "1.5.3", "react" : "0.14.7", "react-dom" : "0.14.7", @@ -34,7 +34,7 @@ "uniq" : "1.0.1", "validator" : "4.5.0", "whatwg-fetch": "0.10.1", - "react-color": "1.3.3" + "react-color": "1.3.6" }, "devDependencies": { "chai": "^2.3.0", diff --git a/src/fftools/js/fftools.js b/src/fftools/js/fftools.js index b17e61a0fd..8418937944 100644 --- a/src/fftools/js/fftools.js +++ b/src/fftools/js/fftools.js @@ -2,7 +2,7 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import get from 'lodash/object/get'; +import {get} from 'lodash'; import React from 'react'; import ReactDOM from 'react-dom'; @@ -15,16 +15,15 @@ import ResultsPanel from 'firefly/ui/ResultsPanel.jsx'; import FormPanel from 'firefly/ui/FormPanel.jsx'; import TestImagePanel from 'firefly/visualize/ui/TestImagePanel.jsx'; import {ExpandedModeDisplay} from 'firefly/visualize/iv/ExpandedModeDisplay.jsx'; -import TablePanel from 'firefly/tables/ui/TablePanel.jsx'; +import {TablePanel} from 'firefly/tables/ui/TablePanel.jsx'; import Validate from 'firefly/util/Validate.js'; -import TblUtil from 'firefly/tables/TableUtil.js'; +import * as TblUtil from 'firefly/tables/TableUtil.js'; import HistogramTableViewPanel from 'firefly/visualize/HistogramTableViewPanel.jsx'; import XYPlotTableViewPanel from 'firefly/visualize/XYPlotTableViewPanel.jsx'; import {VisHeader} from 'firefly/visualize/ui/VisHeader.jsx'; import {VisToolbar} from 'firefly/visualize/ui/VisToolbar.jsx'; import FieldGroup from 'firefly/ui/FieldGroup.jsx'; -import CompleteButton from 'firefly/ui/CompleteButton.jsx'; import ValidationField from 'firefly/ui/ValidationField.jsx'; import {TableRequest} from 'firefly/tables/TableRequest.js'; @@ -32,7 +31,7 @@ import {TableRequest} from 'firefly/tables/TableRequest.js'; import TableStatsCntlr from 'firefly/visualize/TableStatsCntlr.js'; import HistogramCntlr from 'firefly/visualize/HistogramCntlr.js'; import XYPlotCntlr from 'firefly/visualize/XYPlotCntlr.js'; -import TablesCntlr from 'firefly/tables/TablesCntlr.js'; +import * as TablesCntlr from 'firefly/tables/TablesCntlr.js'; import {getRootURL} from 'firefly/util/BrowserUtil.js'; import {download} from 'firefly/util/WebUtil.js'; @@ -79,7 +78,6 @@ const App = React.createClass({ id:'IpacTableFromSource', source: request.srcTable, tbl_id: newActiveTblId(), - pageSize: 50, filters: request.filters }); @@ -101,7 +99,6 @@ const App = React.createClass({ const tblId = table ? table.tbl_id : undefined; const highlightedRow = table ? table.highlightedRow : undefined; - const v = get(this.props, 'appData.props.version') || 'unknown'; if (!appData.isReady) { return (
@@ -155,11 +152,11 @@ const App = React.createClass({
: - } + : + } visToolbar = {} - xyPlot = {
} - tables = { } + xyPlot = {
} + tables = { } layoutInfo = { appData.layoutInfo } />
diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupFilter.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupFilter.java index 141d955794..d9ee2fbc83 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupFilter.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupFilter.java @@ -106,18 +106,6 @@ void start() throws IOException { attributes.add(new DataGroup.Attribute("col." + DataGroup.ROWID_NAME + ".Visibility", "hidden")); } - // combine meta with file attributes - if (meta == null) { - meta = new HashMap<>(attributes.size()); - } else { - for (String k : meta.keySet()) { - attributes.add(new DataGroup.Attribute(k, meta.get(k))); - } - } - for (DataGroup.Attribute at : attributes) { - meta.put(at.getKey(), at.getValue()); - } - DataGroup dg = new DataGroup(null, headers); boolean needToWriteHeader = true; @@ -189,7 +177,7 @@ public void run() { if (CollectionUtil.matches(rowIdx, row, filters)) { row.setRowIdx(rowIdx); IpacTableUtil.writeRow(writer, headers, row); - if (++rowsFound % minPrefetchSize == 0) { + if (++rowsFound % 5000 == 0) { IpacTableUtil.sendLoadStatusEvents(meta, outf, rowsFound, DataGroupPart.State.INPROGRESS); } } diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupReader.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupReader.java index 0261be4424..c87ffb191d 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupReader.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupReader.java @@ -23,7 +23,7 @@ * @version $Id: DataGroupReader.java,v 1.13 2012/11/05 18:59:59 loi Exp $ */ public class DataGroupReader { - public static final int MIN_PREFETCH_SIZE = AppProperties.getIntProperty("IpacTable.min.prefetch.size", 5000); + public static final int MIN_PREFETCH_SIZE = AppProperties.getIntProperty("IpacTable.min.prefetch.size", 500); public static final String LINE_SEP = System.getProperty("line.separator"); private static final Logger.LoggerImpl logger = Logger.getLogger(); diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupWriter.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupWriter.java index 0de01a3d29..95f500ff6a 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupWriter.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/DataGroupWriter.java @@ -130,7 +130,7 @@ public void run() { while(itr.hasNext()) { DataObject row = itr.next(); IpacTableUtil.writeRow(writer, headers, row); - if (++rowCount % minPrefetchSize == 0) { + if (++rowCount % 5000 == 0) { IpacTableUtil.sendLoadStatusEvents(meta, outf, rowCount, DataGroupPart.State.INPROGRESS); } } diff --git a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java index 1a9b2546a9..09a1029660 100644 --- a/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java +++ b/src/firefly/java/edu/caltech/ipac/firefly/server/util/ipactable/JsonTableUtil.java @@ -124,7 +124,6 @@ public static JSONObject toJsonTableMeta(TableMeta meta) { JSONObject tmeta = new JSONObject(); tmeta.put("source", meta.getSource()); tmeta.put("fileSize", meta.getFileSize()); - tmeta.put("isFullyLoaded", meta.isLoaded()); if (meta.getRelatedCols() != null) { tmeta.put("relatedCols", StringUtils.toString(meta.getRelatedCols(), ",")); } diff --git a/src/firefly/java/edu/caltech/ipac/util/IpacTableUtil.java b/src/firefly/java/edu/caltech/ipac/util/IpacTableUtil.java index fbd31ae665..917b54efcc 100644 --- a/src/firefly/java/edu/caltech/ipac/util/IpacTableUtil.java +++ b/src/firefly/java/edu/caltech/ipac/util/IpacTableUtil.java @@ -393,11 +393,11 @@ public static void sendLoadStatusEvents(Map meta, File outf, int String source = meta.get("source") == null ? ServerContext.replaceWithPrefix(outf) : String.valueOf( meta.get("source") ); String tblId = meta.get("tbl_id") == null ? source : String.valueOf( meta.get("tbl_id") ); - FluxAction action = new FluxAction("table-space.loadTableStatus"); + FluxAction action = new FluxAction("table_space.loadTableStatus"); action.setValue(tblId, "tbl_id"); - action.setValue(source, "source"); action.setValue(crows, "totalRows"); action.setValue(state.name(), "tableMeta", DataGroupPart.LOADING_STATUS); + action.setValue(source, "tableMeta", "source"); ServerEventManager.fireAction(action); } diff --git a/src/firefly/js/core/AppDataCntlr.js b/src/firefly/js/core/AppDataCntlr.js index bfa0163bca..8f009cfaf2 100644 --- a/src/firefly/js/core/AppDataCntlr.js +++ b/src/firefly/js/core/AppDataCntlr.js @@ -1,7 +1,7 @@ /* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {pick, isUndefined} from 'lodash'; +import {pickBy, isUndefined} from 'lodash'; import {flux} from '../Firefly.js'; import BrowserCache from '../util/BrowserCache.js'; @@ -322,7 +322,7 @@ function dispatchRemovePreference(name) { * @param hasXyPlots boolean. XY Plot data available. */ function dispatchUpdateLayout({search, results, mode, views, hasTables, hasImages, hasXyPlots}) { - flux.process({type: UPDATE_LAYOUT, payload: pick({search, results, mode, views, hasTables, hasImages, hasXyPlots}, (v)=>(!isUndefined(v)))}); + flux.process({type: UPDATE_LAYOUT, payload: pickBy({search, results, mode, views, hasTables, hasImages, hasXyPlots}, (v)=>(!isUndefined(v)))}); } diff --git a/src/firefly/js/core/History.js b/src/firefly/js/core/History.js index de8da27895..cf16f8395a 100644 --- a/src/firefly/js/core/History.js +++ b/src/firefly/js/core/History.js @@ -3,8 +3,7 @@ */ import {flux} from '../Firefly.js'; -import set from 'lodash/object/set'; -import get from 'lodash/object/get'; +import {set, get} from 'lodash'; var HISTORY_PATH = 'history'; const MAX_HISTORY_LENGTH = 20; diff --git a/src/firefly/js/core/ReduxFlux.js b/src/firefly/js/core/ReduxFlux.js index c7e64f63f7..247793b5ee 100644 --- a/src/firefly/js/core/ReduxFlux.js +++ b/src/firefly/js/core/ReduxFlux.js @@ -17,7 +17,8 @@ import VisMouseCntlr from '../visualize/VisMouseCntlr.js'; import TableStatsCntlr from '../visualize/TableStatsCntlr.js'; import HistogramCntlr from '../visualize/HistogramCntlr.js'; import XYPlotCntlr from '../visualize/XYPlotCntlr.js'; -import TablesCntlr from '../tables/TablesCntlr'; +import * as TablesCntlr from '../tables/TablesCntlr'; +import * as TablesUiCntlr from '../tables/TablesUiCntlr'; import DrawLayer from '../visualize/DrawLayerCntlr.js'; import DrawLayerFactory from '../visualize/draw/DrawLayerFactory.js'; import DrawLayerCntlr, {makeDetachLayerActionCreator} from '../visualize/DrawLayerCntlr.js'; @@ -52,6 +53,7 @@ const reducers = { [HistogramCntlr.HISTOGRAM_DATA_KEY]: HistogramCntlr.reducer, [XYPlotCntlr.XYPLOT_DATA_KEY]: XYPlotCntlr.reducer, [TablesCntlr.TABLE_SPACE_PATH]: TablesCntlr.reducer, + [TablesUiCntlr.TABLE_UI_PATH]: TablesUiCntlr.reducer, [DrawLayer.DRAWING_LAYER_KEY]: DrawLayer.makeReducer(drawLayerFactory) }; @@ -66,10 +68,11 @@ actionCreators.set(ImagePlotCntlr.PLOT_IMAGE, ImagePlotCntlr.plotImageActionCrea actionCreators.set(ImagePlotCntlr.ZOOM_IMAGE, ImagePlotCntlr.zoomActionCreator); actionCreators.set(ImagePlotCntlr.COLOR_CHANGE, ImagePlotCntlr.colorChangeActionCreator); actionCreators.set(ImagePlotCntlr.STRETCH_CHANGE, ImagePlotCntlr.stretchChangeActionCreator); -actionCreators.set(TablesCntlr.FETCH_TABLE, TablesCntlr.fetchTable); -actionCreators.set(TablesCntlr.LOAD_TABLE, TablesCntlr.loadTable); actionCreators.set(DrawLayerCntlr.DETACH_LAYER_FROM_PLOT, makeDetachLayerActionCreator(drawLayerFactory)); +actionCreators.set(TablesCntlr.FETCH_TABLE, TablesCntlr.fetchTable); +actionCreators.set(TablesCntlr.LOAD_TABLE, TablesCntlr.loadTable); +actionCreators.set(TablesUiCntlr.TBL_UI_GOTO_PAGE, TablesUiCntlr.gotoPage); actionCreators.set(TableStatsCntlr.LOAD_TBL_STATS, TableStatsCntlr.loadTblStats); actionCreators.set(HistogramCntlr.LOAD_COL_DATA, HistogramCntlr.loadColData); diff --git a/src/firefly/js/tables/SelectInfo.js b/src/firefly/js/tables/SelectInfo.js index 1c149ccb48..a40e154a4a 100644 --- a/src/firefly/js/tables/SelectInfo.js +++ b/src/firefly/js/tables/SelectInfo.js @@ -108,7 +108,7 @@ export class SelectInfo { * @returns {SelectInfo} */ static newInstance({selectAll=false, exceptions=(new Set()), rowCount=0} = {}) { - return arguments[0] ? new SelectInfo(arguments[0]) : new SelectInfo({selectAll, exceptions, rowCount}); + return new SelectInfo({selectAll, exceptions, rowCount}); } /** @@ -118,7 +118,7 @@ export class SelectInfo { * @returns {SelectInfo} */ static find(tbl_id, root) { - var table = Table.find(tbl_id, root); + var table = Table.findTblById(tbl_id, root); return table && SelectInfo.newInstance(table.selectInfo); } diff --git a/src/firefly/js/tables/Table.js b/src/firefly/js/tables/Table.js index 8f24821eae..31be10c869 100644 --- a/src/firefly/js/tables/Table.js +++ b/src/firefly/js/tables/Table.js @@ -2,16 +2,17 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ +import {get, slice} from 'lodash'; import {flux} from '../Firefly.js'; -import TblCntlr from './TablesCntlr.js'; -import TblUtil from './TableUtil.js'; +import * as TblCntlr from './TablesCntlr.js'; +import * as TblUtil from './TableUtil.js'; /** * */ export class Table { constructor(dataModel) { - this.model = dataModel; + this.data = dataModel; } /** @@ -20,11 +21,20 @@ export class Table { * @returns row of data(array of strings) or null if index is out of range. */ getRow(idx) { - return TblUtil.find(this.model, 'tableData', 'data', idx); + return get(this.data, ['tableData.data', idx]); } getColumn(idx) { - return TblUtil.find(this.model, 'tableData', 'columns', idx); + return get(this.data, ['tableData.columns', idx]); + } + + has(startIdx, endIdx) { + endIdx = endIdx >0 ? Math.min( endIdx, this.data.totalRows) : startIdx; + if (startIdx >=0 && endIdx > startIdx) { + const data = get(this.data, 'tableData.data', []); + const aslice = data.slice(startIdx, endIdx).filter( (v) => v ); + return aslice.length === (endIdx-startIdx); + } else return false; } /** @@ -36,14 +46,14 @@ export class Table { } /** - * return a Table with the data from the application's table-space. + * return a Table with the data from the application's table_space. * @param tbl_id unique table ID - * @param root the application state root. If not given, flux.getState() will be used. + * @param root the data root. If not given, flux.getState()[TblCntlr.TABLE_SPACE_PATH] will be used. * @returns {Table} */ - static find(tbl_id, root) { - const state = root || flux.getState(); - var table = TblUtil.find(state, TblCntlr.TABLE_SPACE_PATH, 'main', tbl_id); + static findTblById(tbl_id, root) { + const state = root || flux.getState()[TblCntlr.TABLE_SPACE_PATH]; + var table = state[tbl_id]; return table && Table.newInstance(table); } } \ No newline at end of file diff --git a/src/firefly/js/tables/TableRequest.js b/src/firefly/js/tables/TableRequest.js index b8fddd6599..d1bdf9084d 100644 --- a/src/firefly/js/tables/TableRequest.js +++ b/src/firefly/js/tables/TableRequest.js @@ -2,7 +2,8 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {pick, identity} from 'lodash'; +import {pickBy} from 'lodash'; +import {uniqueTblId} from './TableUtil.js'; export class TableRequest { @@ -32,8 +33,8 @@ export class TableRequest { * @param copyFromReq * @returns {TableRequest} */ - static newInstance({id, tbl_id, startIdx, pageSize, filters, sortInfo, inclCols, decimate, META_INFO, ...rest}, copyFromReq) { - var params = Object.assign(rest, pick({id, tbl_id, startIdx, pageSize, filters, sortInfo, inclCols, decimate, META_INFO}, identity)); // take only defined params + static newInstance({id, tbl_id=uniqueTblId(), startIdx, pageSize=100, filters, sortInfo, inclCols, decimate, META_INFO, ...rest}, copyFromReq) { + var params = Object.assign(rest, pickBy({id, tbl_id, startIdx, pageSize, filters, sortInfo, inclCols, decimate, META_INFO})); // take only defined params if (copyFromReq) { params = Object.assign(copyFromReq, params); } diff --git a/src/firefly/js/tables/TableStore.js b/src/firefly/js/tables/TableStore.js new file mode 100644 index 0000000000..695fb44331 --- /dev/null +++ b/src/firefly/js/tables/TableStore.js @@ -0,0 +1,210 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ + +import {get, set, isEmpty} from 'lodash'; + +import * as TblCntlr from './TablesCntlr.js'; +import * as TblUiCntlr from './TablesUiCntlr.js'; +import {flux} from '../Firefly.js'; +import * as TblUtil from './TableUtil.js'; +import {Table} from './Table.js'; + +function makeColWidth(tableModel) { + var columns = get(tableModel, 'tableData.columns'); + return !columns ? {} : columns.reduce((widths, col, cidx) => { + const label = col.title || col.name; + var nchar = col.prefWidth; + if (!nchar) { + nchar = Math.max(label.length, get(tableModel, `tableData.data.0.${cidx}.length`) || 0); + } + widths[col.name] = nchar * 8.5; + return widths; + }, {}); +} + +export class TableStore { + constructor(component) { + this.component = component; + this.changeListener = undefined; + this.isRemoteStore = false; + } + + setChangeListener(callback) { + this.changeListener = callback; + } + + updateFromFlux() { + var {tbl_id, tbl_ui_id, tbl_ui_gid} = this.component.state.tableUi; + var tableModel = TblUtil.findById(tbl_id); + if ( tableModel.tableData ) { + var tableUi = TblUtil.findUiById(tbl_ui_id, tbl_ui_gid); + if (tableModel !== this.component.state.tableModel || tableUi !== this.component.state.tableUi) { + var nState = this.prepareState(tableModel, tableUi); + nState && this.changeListener && this.changeListener(nState); + } + } + } + + removeChangeListener() { + if (this.isRemoteStore) { + this.removeListener && this.removeListener(); + } + } + + receiveProps({tableModel, tbl_id, tbl_ui_gid, pageSize}) { + var {tableUi} = this.component.state; + tableUi.pageSize = pageSize || tableUi.pageSize; + if (tbl_id && tbl_id !== tableUi.tbl_id) { + this.isRemoteStore = true; + Object.assign(tableUi, {tbl_id, tbl_ui_gid}); + if (!this.removeListener) { + this.removeListener= flux.addListener(() => this.updateFromFlux()); + } + tableModel = TblUtil.findById(tbl_id); + } + if (tableModel) { + if (tableModel !== this.component.state.tableModel) { + tableUi.tbl_id = tableModel.tbl_id; + this.component.setState(this.prepareState(tableModel, tableUi)); + } + } + } + + onResize(size) { + if (size) { + var {showToolbar} = this.component.props; + var tableUi = Object.assign({},this.component.state.tableUi); + const pagingBarHeight = showToolbar ? 26 : 0; + tableUi.widthPx = size.width-9; + tableUi.heightPx = size.height-pagingBarHeight-6; + this.handleEvent(TblUiCntlr.TBL_UI_RESIZE, {tableUi}); + } + } + + onColumnResize(columnWidths) { + var tableUi = Object.assign({},this.component.state.tableUi); + tableUi.columnWidths = Object.assign(tableUi.columnWidths, columnWidths); + this.handleEvent(TblUiCntlr.TBL_UI_COL_RESIZE, {tableUi}); + } + + changePageSize(pageSize) { + var tableUi = Object.assign({},this.component.state.tableUi); + var tableModel = Object.assign({},this.component.state.tableModel); + if (pageSize != tableUi.pageSize) { + tableUi.pageSize = pageSize; + tableUi.currentPage = 1; + tableModel.highlightedRow = 0; + this.handleEvent(TblUiCntlr.TBL_UI_GOTO_PAGE, {tableModel, tableUi}); + } + } + + gotoPage(number=1, hlRowIdx=0) { + var tableUi = Object.assign({},this.component.state.tableUi); + var tableModel = Object.assign({},this.component.state.tableModel); + if (number != tableUi.currentPage && number > 0 && number <= this.component.state.totalPages) { + tableUi.currentPage = number; + tableUi.hlRowIdx = hlRowIdx; + tableModel.highlightedRow = this.toAbsIdx(hlRowIdx, tableUi); + this.handleEvent(TblCntlr.TBL_HIGHLIGHT_ROW, {tableModel, tableUi}); + } + } + + onRowHighlight(rowIdx) { + if (rowIdx !== this.component.state.tableUi.hlRowIdx) { + var tableModel = Object.assign({},this.component.state.tableModel); + tableModel.highlightedRow = this.toAbsIdx(rowIdx); + this.handleEvent(TblCntlr.TBL_HIGHLIGHT_ROW, {tableModel}); + } + } + + onSelectAll(checked, selectInfo) { + if (checked) { + selectInfo.selectAll(); + } else { + selectInfo.deselectAll(); + } + var {tbl_id} = this.component.state.tableModel; + var changes = { tbl_id, selectionInfo:selectInfo.data }; + this.handleEvent(TblCntlr.TBL_SELECT_ROW, {tableModel:changes}); + } + + onRowSelect(checked, rowIndex, selectInfo) { + let absIdx = this.toAbsIdx(rowIndex); + if (checked) { + selectInfo.select(absIdx); + } else { + selectInfo.deselect(absIdx); + } + var {tbl_id} = this.component.state.tableModel; + var changes = { tbl_id, selectionInfo:selectInfo.data }; + this.handleEvent(TblCntlr.TBL_SELECT_ROW, {tableModel:changes}); + } + + toAbsIdx(relIdx, tableUi) { + var {currentPage, pageSize} = tableUi || this.component.state.tableUi; + return (currentPage-1) * pageSize + relIdx; + } + + toRelIdx(absIdx, pageSize=this.component.state.tableUi.pageSize) { + return absIdx % pageSize; + } + + prepareState(tableModel, tableUi) { + tableModel = tableModel || Object.assign({}, this.component.state.tableModel); + tableUi = tableUi || this.component.state.tableUi; + + if (!tableModel.tableData.columns) return {tableModel, tableUi}; + + var {sortInfo, selectionInfo, filterInfo, highlightedRow} = tableModel; + var {pageSize, currentPage, widthPx, heighPx, columns} = tableUi; + + if (isEmpty(columns) || tableUi.columns !== this.component.state.tableUi.columns) { + tableUi.columns = get(tableModel, 'tableData.columns'); + tableUi.columnWidths = makeColWidth(tableModel); + } + + tableUi.currentPage = highlightedRow >= 0 ? Math.floor(highlightedRow / pageSize)+1 : 1; + tableUi.hlRowIdx = highlightedRow >= 0 ? this.toRelIdx(highlightedRow) : 0; + const startIdx = (tableUi.currentPage-1) * pageSize; + const endIdx = Math.min(startIdx+pageSize, tableModel.totalRows) || startIdx ; + var totalPages = Math.ceil((tableModel.totalRows || 0)/pageSize); + var data = []; + if ( Table.newInstance(tableModel).has(startIdx, endIdx) ) { + data = tableModel.tableData.data.slice(startIdx, endIdx); + } else { + Object.assign(tableModel.request, {startIdx, pageSize}); + TblCntlr.dispatchFetchTable(tableModel.request, highlightedRow); + } + var tableRowCount = data.length; + + return {tableModel, tableUi, totalPages, tableRowCount, data}; + } + + handleEvent(type, payload) { + if (this.isRemoteStore) { + switch (type) { + case (TblCntlr.TBL_SELECT_ROW) : + case (TblCntlr.TBL_HIGHLIGHT_ROW) : + flux.process({type, payload: payload.tableModel}); + break; + + default: + flux.process({type, payload: payload.tableUi}); + } + + } else { + var tableModel = Object.assign({}, this.component.state.tableModel, payload.tableModel); + var tableUi = Object.assign({}, this.component.state.tableUi, payload.tableUi); + var nState = this.prepareState(tableModel, tableUi); + this.changeListener && this.changeListener(nState); + } + } + + /** + * @returns {TableStore} + */ + static newInstance(component) { + return new TableStore(component); + } +} diff --git a/src/firefly/js/tables/TableUtil.js b/src/firefly/js/tables/TableUtil.js index daaf37c17c..50dd09228f 100644 --- a/src/firefly/js/tables/TableUtil.js +++ b/src/firefly/js/tables/TableUtil.js @@ -2,33 +2,71 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ -import {get, set} from 'lodash'; -import isBlank from 'underscore.string/isBlank'; -import TblCntlr from './TablesCntlr.js'; +import {get, isEmpty, uniqueId} from 'lodash'; +import * as TblCntlr from './TablesCntlr.js'; +import * as TblUiCntlr from './TablesUiCntlr.js'; +import {SelectInfo} from './SelectInfo.js'; import {flux} from '../Firefly.js'; +import {fetchUrl} from '../util/WebUtil.js'; +import { getRootPath } from '../util/BrowserUtil.js'; +import {TableRequest} from './TableRequest.js'; -function doValidate(type, action) { +const SRV_PATH = getRootPath() + 'search/json'; +const INT_MAX = Math.pow(2,31) - 1; + +/** + * + * @param tableRequest is a TableRequest params object + * @param hlRowIdx set the highlightedRow. default to startIdx. + * @returns {Promise.} + */ +export function doFetchTable(tableRequest, hlRowIdx) { + const def = { + startIdx: 0, + pageSize : INT_MAX, + tbl_id : (tableRequest.tbl_id || tableRequest.title || tableRequest.id) + }; + var params = Object.assign(def, tableRequest); + + return fetchUrl(SRV_PATH, {params}).then( (response) => { + return response.json().then( (tableModel) => { + const startIdx = get(tableModel, ['request',TableRequest.keys.startIdx], 0); + if (startIdx > 0) { + // shift data arrays indices to match partial fetch + tableModel.tableData.data = tableModel.tableData.data.reduce( (nAry, v, idx) => { + nAry[idx+startIdx] = v; + return nAry; + }, []); + } + tableModel.highlightedRow = hlRowIdx || startIdx; + if (!tableModel.selectionInfo) { + tableModel.selectionInfo = SelectInfo.newInstance({rowCount:tableModel.totalRows}).data; + } + return tableModel; + }); + }); +} + +export function doValidate(type, action) { if (type !== action.type) { error(action, `Incorrect type:${action.type} was sent to a ${type} actionCreator.`); } if (!action.payload) { error(action, 'Invalid action. Payload is missing.'); } + var {request} = action.payload; if (type === TblCntlr.FETCH_TABLE ) { - if (isBlank(action.payload.id)) { + if (isEmpty(request.id)) { error(action, 'Required "id" field is missing.'); } - } else { - if (isBlank(action.payload.tbl_id)) { + if (isEmpty(request.tbl_id)) { error(action, 'Required "tbl_id" field is missing.'); } - if(type === TblCntlr.TBL_HIGHLIGHT_ROW) { - const idx = action.payload.highlightedRow; - if (!idx || idx<0) { - error(action, 'highlightedRow must be a positive number.'); - } + } else if(type === TblCntlr.TBL_HIGHLIGHT_ROW) { + const idx = action.payload.highlightedRow; + if (!idx || idx<0) { + error(action, 'highlightedRow must be a positive number.'); } - } return action; } @@ -39,44 +77,42 @@ function doValidate(type, action) { * @param action the actoin to update * @param cause the error to be added. */ -function error(action, cause) { +export function error(action, cause) { (action.err = action.err || []).push(cause); } -function findById(id, space='main') { +export function findById(id) { var tableSpace = flux.getState()[TblCntlr.TABLE_SPACE_PATH]; - return find(tableSpace, space,id); + return get(tableSpace, id); } /** - * put a table model object into the given application state. - * @param tableSpace - * @param value the table model object to put + * find table ui info by tbl_ui_id and tbl_ui_gid + * @param tid + * @param gid * @returns {*} */ -function put(tableSpace, value, space='main') { - return set(tableSpace, space + '.' + value.tbl_id, value); +export function findUiById(tbl_ui_id, tbl_ui_gid) { + return get(flux.getState(), [TblUiCntlr.TABLE_UI_PATH, tbl_ui_gid, tbl_ui_id]); } /** - * find the object at the given paths. - * @param data data root. find will start from here. - * @param paths an array of path elements. - * @returns {*} an object or undefined if paths does not exist under the data root. + * return true if the table referenced by the given tbl_id is fully loaded. + * @param tbl_id + * @returns {boolean} */ -function find(data, ...paths) { - return data ? get(data, paths) : null; +export function isFullyLoaded(tbl_id) { + return isTableLoaded(findById(tbl_id)); } - -function isFullyLoaded(id, space='main') { - const table = findById(id, space); - if (table && table.model) { - if (table.model.tableMeta.isFullyLoaded) { - return true; - } - } - return false; +/** + * return true if the given table is fully loaded. + * @param tableModel + * @returns {boolean} + */ +export function isTableLoaded(tableModel) { + const status = tableModel && get(tableModel, 'tableMeta.Loading-Status', 'COMPLETED'); + return status === 'COMPLETED'; } /** @@ -86,7 +122,7 @@ function isFullyLoaded(id, space='main') { * @param tableModel * @returns {*} */ -function transform(tableModel) { +export function transform(tableModel) { if (tableModel.tableData && tableModel.tableData.data) { const cols = tableModel.tableData.columns; @@ -100,12 +136,57 @@ function transform(tableModel) { } } -export default { - error, - doValidate, - isFullyLoaded, - findById, - put, - find, - transform -}; \ No newline at end of file +/** + * This function merges the source object into the target object + * by traversing and comparing every like path. If a value was + * merged at any data node in the data graph, the node and all of its + * parent nodes will be shallow cloned and returned. Otherwise, the target's value + * will be returned. + * @param target + * @param source + * @returns {*} + */ +export function smartMerge(target, source) { + if (!target) return source; + + if ( source && typeof(source)=='object') { + if(source instanceof Array) { + let aryChanges = []; + source.forEach( (v, idx) => { + const nval = smartMerge(target[idx], source[idx]); + if (nval !== target[idx]) { + aryChanges[idx] = nval; + } + }); + if (isEmpty(aryChanges)) return target; + else { + let nAry = target.slice(); + aryChanges.forEach( (v, idx) => nAry[idx] = v ); + return nAry; + } + } else { + let objChanges = {}; + Object.keys(source).forEach( (k) => { + const nval = smartMerge(target[k], source[k]); + if (nval !== target[k]) { + objChanges[k] = nval; + } + }); + return (isEmpty(objChanges)) ? target : Object.assign({}, target, objChanges); + } + } else { + return (target == source) ? target : source; + } +} + +export function uniqueTblId() { + return uniqueId('tbl_id-'); +} + +export function uniqueTblUiId() { + return uniqueId('tbl_ui_id-'); +} + +export function uniqueTblUiGid() { + return uniqueId('tbl_ui_gid-'); +} \ No newline at end of file diff --git a/src/firefly/js/tables/TablesCntlr.js b/src/firefly/js/tables/TablesCntlr.js index 2700edd5a2..99b953829a 100644 --- a/src/firefly/js/tables/TablesCntlr.js +++ b/src/firefly/js/tables/TablesCntlr.js @@ -1,42 +1,41 @@ /* * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ +import update from 'react-addons-update'; +import {set, get, isEqual} from 'lodash'; import {flux} from '../Firefly.js'; -import {fetchUrl, logError} from '../util/WebUtil.js'; -import { getRootPath } from '../util/BrowserUtil.js'; -import TblUtil from './TableUtil.js'; -import LoadTable from './reducers/LoadTable.js'; - -const TABLE_SPACE_PATH = 'table-space'; -const MAIN_SPACE_PATH = `${TABLE_SPACE_PATH}.main`; +import * as TblUtil from './TableUtil.js'; +import {logError} from '../util/WebUtil.js'; +export const TABLE_SPACE_PATH = 'table_space'; /*---------------------------- ACTIONS -----------------------------*/ -const FETCH_TABLE = `${TABLE_SPACE_PATH}.fetchTable`; -const LOAD_TABLE = `${TABLE_SPACE_PATH}.loadTable`; -const LOAD_TABLE_STATUS = `${TABLE_SPACE_PATH}.loadTableStatus`; -const LOAD_TABLE_COMPLETE = `${TABLE_SPACE_PATH}.loadTableComplete`; +export const FETCH_TABLE = `${TABLE_SPACE_PATH}.fetchTable`; +export const LOAD_TABLE = `${TABLE_SPACE_PATH}.loadTable`; +export const LOAD_TABLE_STATUS = `${TABLE_SPACE_PATH}.loadTableStatus`; +export const LOAD_TABLE_COMPLETE = `${TABLE_SPACE_PATH}.loadTableComplete`; -const TBL_SELECT_ROW = `${TABLE_SPACE_PATH}.tblSelectRow`; -const TBL_HIGHLIGHT_ROW = `${TABLE_SPACE_PATH}.tblHighlighRow`; +export const TBL_SELECT_ROW = `${TABLE_SPACE_PATH}.selectRow`; +export const TBL_HIGHLIGHT_ROW = `${TABLE_SPACE_PATH}.highlighRow`; /*---------------------------- CREATORS ----------------------------*/ -function loadTable(action) { +export function loadTable(action) { return validate(LOAD_TABLE, action); } -function fetchTable(action) { +export function fetchTable(action) { return (dispatch) => { - dispatch(validate(FETCH_TABLE, action)); + //dispatch(validate(FETCH_TABLE, action)); if (!action.err) { - LoadTable.doFetchTable(action.payload).then ( (tableModel) => { + var {request, hlRowIdx} = action.payload; + TblUtil.doFetchTable(request, hlRowIdx).then ( (tableModel) => { dispatch( loadTable({type:LOAD_TABLE, payload: tableModel}) ); }).catch( (error) => { logError(error); // if fetch causes error, re-dispatch that same action with error msg. - action.err = error; + TblUtil.error(error); dispatch(action); }); } @@ -45,29 +44,33 @@ function fetchTable(action) { /*---------------------------- REDUCERS -----------------------------*/ -function reducer(state={}, action={}) { - var newState = Object.assign({}, state); - +export function reducer(state={}, action={}) { + var inTable; + const {tbl_id, selectionInfo, highlightedRow} = action.payload || {}; switch (action.type) { - case (TBL_HIGHLIGHT_ROW) : case (TBL_SELECT_ROW) : + if (selectionInfo) { + return update(state, { [tbl_id] : {selectionInfo: {$set: selectionInfo}}}); + } else return state; + + case (TBL_HIGHLIGHT_ROW) : + if (highlightedRow >= 0 && highlightedRow !== get(state, [tbl_id, 'highlightedRow'])) { + return update(state, { [tbl_id] : {highlightedRow: {$set: highlightedRow}}}); + } else return state; + case (LOAD_TABLE_STATUS) : case (LOAD_TABLE_COMPLETE) : case (LOAD_TABLE) : - newState = LoadTable.reducer(newState, action); - break; + inTable = action.payload; + return TblUtil.smartMerge(state, {[inTable.tbl_id] : inTable}); + case (FETCH_TABLE) : - var tmpAction = {'type' : LOAD_TABLE, 'payload': {tbl_id : action.payload.tbl_id, tableMeta : {'isLoading' : true}} }; - newState = LoadTable.reducer(newState, tmpAction); - if (tmpAction.err) { - TblUtil.error(action, tmpAction.err); - } - break; + inTable = { tbl_id : action.payload.tbl_id, tableMeta : {'isLoading' : true}}; + return TblUtil.smartMerge(state, {[inTable.tbl_id] : inTable}); default: return state; } - return newState; } /*---------------------------- DISPATCHERS -----------------------------*/ @@ -79,16 +82,17 @@ function reducer(state={}, action={}) { * Update will always attempt to merge the data, regardless of partial or complete. * @param tableModel the dataModel to load. */ -function dispatchLoadTable(tableModel) { +export function dispatchLoadTable(tableModel) { flux.process( {type: LOAD_TABLE, payload: {tableModel}}); } /** * Fetch a table from the server. - * @param tableRequest a TableRequest params object. + * @param request a TableRequest params object. + * @param hlRowIdx set the highlightedRow. default to startIdx. */ -function dispatchFetchTable(tableRequest) { - flux.process( {type: FETCH_TABLE, payload: tableRequest }); +export function dispatchFetchTable(request, hlRowIdx) { + flux.process( {type: FETCH_TABLE, payload: {request, hlRowIdx} }); } /** @@ -96,7 +100,7 @@ function dispatchFetchTable(tableRequest) { * @param tbl_id * @param highlightedRow */ -function dispatchHighlightRow(tbl_id, highlightedRow) { +export function dispatchHighlightRow(tbl_id, highlightedRow) { flux.process( {type: TBL_HIGHLIGHT_ROW, payload: {tbl_id, highlightedRow} }); } @@ -105,32 +109,11 @@ function dispatchHighlightRow(tbl_id, highlightedRow) { * @param tbl_id * @param selectInfo */ -function dispatchRowSelect(tbl_id, selectInfo) { +export function dispatchRowSelect(tbl_id, selectInfo) { flux.process( {type: TBL_SELECT_ROW, payload: {tbl_id, selectInfo} }); } -/*---------------------------- EXPORTS -----------------------------*/ -export default { - TABLE_SPACE_PATH, - MAIN_SPACE_PATH, - FETCH_TABLE, - LOAD_TABLE, - LOAD_TABLE_STATUS, - LOAD_TABLE_COMPLETE, - TBL_SELECT_ROW, - TBL_HIGHLIGHT_ROW, - reducer, - dispatchLoadTable, - dispatchFetchTable, - dispatchHighlightRow, - dispatchRowSelect, - fetchTable, - loadTable -}; - - - /*---------------------------- PRIVATE -----------------------------*/ /** diff --git a/src/firefly/js/tables/TablesUiCntlr.js b/src/firefly/js/tables/TablesUiCntlr.js new file mode 100644 index 0000000000..dafb065074 --- /dev/null +++ b/src/firefly/js/tables/TablesUiCntlr.js @@ -0,0 +1,82 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ + +import update from 'react-addons-update'; +import {pickBy, get, isEmpty} from 'lodash'; + +import {flux} from '../Firefly.js'; +import {smartMerge} from './TableUtil.js'; +import {Table} from './Table.js'; +import * as TblUtil from './TableUtil.js'; +import * as TablesCntlr from './TablesCntlr.js'; + + +export const TABLE_UI_PATH = 'table_ui'; + + +/*---------------------------- ACTIONS -----------------------------*/ +export const TBL_UI_ADDED = `${TABLE_UI_PATH}.uiAdded`; +export const TBL_UI_GOTO_PAGE = `${TABLE_UI_PATH}.uiGotoPage`; +export const TBL_UI_RESIZE = `${TABLE_UI_PATH}.resize`; +export const TBL_UI_COL_RESIZE = `${TABLE_UI_PATH}.colResize`; +/*---------------------------- CREATORS ----------------------------*/ + +export function gotoPage(action) { + return (dispatch) => { + + if (action.payload) { + var {tbl_id, currentPage, pageSize, hlRowIdx} = action.payload; + const startIdx = (currentPage-1) * pageSize; + const endIdx = startIdx + pageSize; + var table = Table.findTblById(tbl_id); + if (table && table.has(startIdx, endIdx)) { + TablesCntlr.dispatchHighlightRow(tbl_id, startIdx+hlRowIdx); + } else { + const request = Object.assign({}, table.data.request, {startIdx, pageSize}); + TblUtil.doFetchTable(request, startIdx+hlRowIdx).then ( (tableModel) => { + dispatch( TablesCntlr.loadTable({type:TablesCntlr.LOAD_TABLE, payload: tableModel}) ); + }).catch( (error) => { + TblUtil.error(error); + // if fetch causes error, re-dispatch that same action with error msg. + action.err = error; + }); + } + } + }; +} + + +/*---------------------------- REDUCERS -----------------------------*/ +export function reducer(state={}, action={}) { + if (!action || !action.payload) return state; + + switch (action.type) { + case (TBL_UI_ADDED) : + case (TBL_UI_RESIZE) : + case (TBL_UI_GOTO_PAGE) : + case (TBL_UI_COL_RESIZE): + const {tbl_ui_id, tbl_ui_gid} = action.payload; + if (tbl_ui_gid && tbl_ui_id) { + return update(state, { [tbl_ui_gid] : {$set: {[tbl_ui_id]: action.payload}}}); + } + break; + + default: + return state; + } +} + +/*---------------------------- DISPATCHERS -----------------------------*/ + +/** + * Notify flux that a TablePanel was added. + * @param tbl_ui_gid unique group id of the TablePanel + * @param tbl_ui_id unique id of the TablePanel + * @param tbl_id tbl_id of the table data being served + */ +export function dispatchUiAdded({tbl_ui_gid, tbl_ui_id, tbl_id, ...rest}) { + var payload = Object.assign({}, pickBy({tbl_ui_gid, tbl_ui_id, tbl_id, ...rest})); // take only defined params + flux.process( {type: TBL_UI_ADDED, payload}); +} + diff --git a/src/firefly/js/tables/__test__/TablesCntlr-test.js b/src/firefly/js/tables/__test__/TablesCntlr-test.js index a997ddde2a..97f8e2b027 100644 --- a/src/firefly/js/tables/__test__/TablesCntlr-test.js +++ b/src/firefly/js/tables/__test__/TablesCntlr-test.js @@ -4,9 +4,7 @@ import {expect} from 'chai'; import {assert} from 'chai'; import {TableRequest} from '../TableRequest.js'; -import rewire from 'rewire'; - -var doFetchTable = rewire('../reducers/LoadTable.js').__get__('doFetchTable'); +import {doFetchTable} from '../TableUtil.js'; describe('A test suite for tables/TablesCntlr.js', function () { var request; diff --git a/src/firefly/js/tables/reducers/LoadTable.js b/src/firefly/js/tables/reducers/LoadTable.js deleted file mode 100644 index f741771f45..0000000000 --- a/src/firefly/js/tables/reducers/LoadTable.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt - */ - -import {fetchUrl} from '../../util/WebUtil.js'; -import { getRootPath } from '../../util/BrowserUtil.js'; -import TblCntlr from '../TablesCntlr.js'; -import TblUtil from '../TableUtil.js'; -import {SelectInfo} from '../SelectInfo.js'; - -const SRV_PATH = getRootPath() + 'search/json'; -const INT_MAX = Math.pow(2,31) - 1; - -/** - * reducer for LOAD_TABLE action. - * TableModel data will be added if one does not exist. Otherwise, it will update the existing - * TableModel via a merge. - * @param state the object root of TABLE_SPACE_PATH. - * @param action LOAD_TABLE action. action's payload should be a TableModel object. - * @returns {*} - */ -function reducer(state={}, action={}) { - - switch (action.type) { - case (TblCntlr.LOAD_TABLE) : - case (TblCntlr.TBL_HIGHLIGHT_ROW) : - case (TblCntlr.LOAD_TABLE_STATUS) : - return mergeTable(state, action.payload); - break; - - case (TblCntlr.LOAD_TABLE_COMPLETE) : - - default: - return state; - } - -} - -function mergeTable(state, newTable) { - var table = TblUtil.find(state, 'main', newTable.tbl_id); - if (table) { - newTable = Object.assign({}, table, newTable); - } - newTable.selectInfo = newTable.selectInfo || {selectAll: false, exceptions: new Set(), rowCount: newTable.totalRows}; - TblUtil.put(state, newTable, 'main', newTable.tbl_id); - return state; -} - - -/** - * - * @param tableRequest is a TableRequest params object - * @returns {Promise.} - */ -function doFetchTable(tableRequest) { - const def = { - startIdx: 0, - pageSize : INT_MAX, - tbl_id : (tableRequest.tbl_id || tableRequest.title || tableRequest.id) - }; - var params = Object.assign(def, tableRequest); - - return fetchUrl(SRV_PATH, {params}).then( (response) => { - return response.json().then( (tableModel) => { - //TblUtil.transform(tableModel); - return tableModel; - }); - }); -} - -export default { - reducer, - doFetchTable -}; \ No newline at end of file diff --git a/src/firefly/js/tables/ui/TablePanel.css b/src/firefly/js/tables/ui/TablePanel.css index 2c943b5583..3ebad9387d 100644 --- a/src/firefly/js/tables/ui/TablePanel.css +++ b/src/firefly/js/tables/ui/TablePanel.css @@ -8,6 +8,34 @@ background-color: #d3fad1 } +.TablePanel__wrapper { + background-color: #a5a5a5; + border: 1px solid #8c8c8c; + padding: 0 3px 3px 3px; +} + +button.paging_bar { + height: 16px; + width: 16px; + margin-top: 3px; +} + +button.paging_bar.first { + background-image: url("/images/icons-2014/16x16_BackwardToEnd.png"); +} + +button.paging_bar.last { + background-image: url("/images/icons-2014/16x16_ForwardToEnd.png"); +} + +button.paging_bar.next { + background-image: url("/images/icons-2014/16x16_Forward.png"); +} + +button.paging_bar.previous { + background-image: url("/images/icons-2014/16x16_Backward.png"); +} + /** below are taken from FixedDataTable with some modifications */ @@ -40,7 +68,7 @@ } .public_fixedDataTable_main, .public_fixedDataTable_header, .public_fixedDataTable_hasBottomBorder { - border-color: #d3d3d3 + border-color: #8c8c8c } .public_fixedDataTable_header .public_fixedDataTableCell_main { @@ -50,7 +78,8 @@ .public_fixedDataTable_header, .public_fixedDataTable_header .public_fixedDataTableCell_main { background-color: #f6f7f8; background-image: -webkit-linear-gradient(#fff, #efefef); - background-image: linear-gradient(#fff, #efefef) + background-image: linear-gradient(#fff, #efefef); + text-align: center; } .public_fixedDataTable_footer .public_fixedDataTableCell_main { @@ -71,7 +100,6 @@ } .public_fixedDataTableCell_main { - /*background-color: #fff;*/ border-color: #d3d3d3; } @@ -319,7 +347,8 @@ body[dir="rtl"] .fixedDataTableColumnResizerLineLayout_main, .fixedDataTableColu .fixedDataTableLayout_rowsContainer { overflow: hidden; - position: relative + position: relative; + background-color: whitesmoke; } .fixedDataTableLayout_horizontalScrollbar { diff --git a/src/firefly/js/tables/ui/TablePanel.jsx b/src/firefly/js/tables/ui/TablePanel.jsx index b33cb1da89..775ad20e77 100644 --- a/src/firefly/js/tables/ui/TablePanel.jsx +++ b/src/firefly/js/tables/ui/TablePanel.jsx @@ -4,193 +4,206 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import TblUtil from '../TableUtil.js'; -import TblCntlr from '../TablesCntlr'; -import {SelectInfo} from '../SelectInfo'; import FixedDataTable from 'fixed-data-table'; import Resizable from 'react-component-resizable'; -import {debounce} from 'lodash'; +import {debounce, get, uniqueId, isEmpty} from 'lodash'; + +import * as TblUtil from '../TableUtil.js'; +import * as TblCntlr from '../TablesCntlr'; +import * as TblUiCntlr from '../TablesUiCntlr.js'; +import {SelectInfo} from '../SelectInfo'; +import {Toolbar} from '../../ui/Toolbar.jsx'; +import {TableStore} from '../TableStore.js'; + import './TablePanel.css'; +import LOADING from 'html/images/gxt/loading.gif'; const {Table, Column, Cell} = FixedDataTable; -const TextCell = ({rowIndex, data, col, ...props}) => { - const val = (data[rowIndex] && data[rowIndex][col]) ? data[rowIndex][col] : 'undef'; +const TextCell = ({rowIndex, data, col, ...rest}) => { return ( - - {val} + + {get(data, [rowIndex, col],'undef')} ); }; -function makeColWidth (tableModel) { - var columns = TblUtil.find(tableModel, 'tableData', 'columns'); - return !columns ? {} : columns.reduce( (widths, col, cidx) => { - const label = col.title || col.name; - widths[col.name] = col.prefWidth || (label.length * 12); - return widths; - }, {}); +function makeColumns(tableStore, columns, columnWidths, data, selectable, selectionInfo) { + if (!columns) return false; + var colsEl = columns.map((col, idx) => { + return ( + {col.title || col.name}} + cell={} + fixed={false} + width={columnWidths[col.name]} + isResizable={true} + allowCellsRecycling={true} + /> + ) + }); + if (selectable) { + var selectInfo = SelectInfo.newInstance(selectionInfo); + const headerCB = () => { + return ( + tableStore.onSelectAll(e.target.checked, selectInfo)}/> + ); + }; + + const cellCB = ({rowIndex}) => { + let absIdx = tableStore.toAbsIdx(rowIndex); + return ( + tableStore.onRowSelect(e.target.checked, rowIndex, selectInfo)}/> + ); + }; + + var cbox = ; + colsEl.splice(0, 0, cbox); + } + return colsEl; } -class TablePanel extends React.Component { +export class TablePanel extends React.Component { constructor(props) { super(props); - + this.tableStore = TableStore.newInstance(this); this.state = { - widthPx: 200, - heightPx: 100, - columnWidths: {} + tableModel: undefined, + tableUi: { + tbl_id: undefined, + tbl_ui_id: TblUtil.uniqueTblUiId(), + tbl_ui_gid: undefined, + pageSize : 1, + currentPage : 1, + widthPx: 300, + heightPx: 100, + hlRowIdx:0, // this is the UI hlrow. its index is relative to only current page. + columns:[], + columnWidths : {} + }, + totalPages: 0, + tableRowCount : 0, + data: [] }; - this.onResize = debounce( (size) => { - if (size) { - this.setState({ widthPx: size.width, heightPx: size.height }); - } + this.onResize = debounce((size) => { + this.tableStore.onResize(size); }, 200); this.onColumnResizeEndCallback = this.onColumnResizeEndCallback.bind(this); - this.makeColumns = this.makeColumns.bind(this); this.rowClassName = this.rowClassName.bind(this); - this.onRowHighlight = this.onRowHighlight.bind(this); - this.onRowSelect = this.onRowSelect.bind(this); - this.onSelectAll = this.onSelectAll.bind(this); + this.storeUpdate = this.storeUpdate.bind(this); } onColumnResizeEndCallback(newColumnWidth, columnKey) { - this.setState(({columnWidths}) => ({ - columnWidths: { - ...columnWidths, - [columnKey]: newColumnWidth, - } - })); + this.tableStore.onColumnResize({[columnKey] : newColumnWidth}); } - makeColumns(tableModel, selectable) { - var columns = TblUtil.find(tableModel, 'tableData', 'columns'); - if (!columns) return false; - var colsEl = columns.map((col, idx) => { - return ( - {col.title || col.name}} - cell={} - fixed={false} - width={this.state.columnWidths[col.name]} - isResizable={true} - allowCellsRecycling={true} - /> - ) - }); - if (selectable) { - var selectInfo = SelectInfo.newInstance(tableModel.selectInfo); - const headerCB = () => { - return ( - this.onSelectAll(e, selectInfo)}/> - ); - } ; - - const cellCB = ({rowIndex}) => { - return ( - this.onRowSelect(e, rowIndex, selectInfo)}/> - ); - } ; - - var cbox = ; - colsEl.splice(0, 0, cbox); - } - return colsEl; + rowClassName(index) { + return (this.state.tableUi.hlRowIdx === index) ? 'tablePanel__Row_highlighted' : ''; } - onSelectAll(e, selectInfo) { - if (e.target.checked) { - selectInfo.selectAll(); - } else { - selectInfo.deselectAll(); - } - var {tableModel} = this.props; - if (tableModel) { - TblCntlr.dispatchRowSelect(tableModel.tbl_id, selectInfo); - } + componentDidMount() { + this.tableStore.setChangeListener(this.storeUpdate); } - onRowSelect(e, rowIndex, selectInfo) { - if (e.target.checked) { - selectInfo.select(rowIndex); - } else { - selectInfo.deselect(rowIndex); - } - var {tableModel} = this.props; - if (tableModel) { - TblCntlr.dispatchRowSelect(tableModel.tbl_id, selectInfo); - } + componentWillUnmount() { + this.tableStore.removeChangeListener(); } - onRowHighlight(e, index) { - var {tableModel} = this.props; - if (tableModel) { - TblCntlr.dispatchHighlightRow(tableModel.tbl_id, index); - } + componentWillReceiveProps(nProps) { + this.tableStore.receiveProps(nProps); } - rowClassName(index) { - var {tableModel} = this.props; - const hlrow = tableModel.highlightedRow || 0; - return (hlrow === index) ? 'tablePanel__Row_highlighted' : ''; + shouldComponentUpdate(nProps, nState) { + return nState !== this.state; } - componentWillUpdate(nProps, nContext) { - if (Object.keys(this.state.columnWidths).length == 0) { - this.state.columnWidths = makeColWidth(nProps.tableModel); - } - } - - componentDidMount() { - this.onResize(); + storeUpdate(state) { + this.setState(state); } render() { - var {tableModel, showFilters, selectable, width, height} = this.props; - var {widthPx, heightPx} = this.state; - width = width || '100%'; + const {tableUi, tableModel, tableRowCount, totalPages, data} = this.state; + const {selectable} = this.props; + if (isEmpty(tableUi.columns)) return false; + + const showLoading = !TblUtil.isTableLoaded(tableModel); + const rowFrom = (tableUi.currentPage-1) * tableUi.pageSize + 1; + const rowTo = rowFrom + tableRowCount - 1; - if (!tableModel || !tableModel.tableData) return (
); return ( - - - {this.makeColumns(tableModel, selectable)} -
+ +
+ +
    +
  • +
+
    +
  • +
  • +
  • e.target.select()} onChange={(e) => this.tableStore.gotoPage(e.target.value)} name='pageNo' size="2" value={tableUi.currentPage}/> of {totalPages}
  • +
  • +
  • +
  • ({rowFrom.toLocaleString()} - {rowTo.toLocaleString()} of {tableModel.totalRows.toLocaleString()})
  • + {showLoading ? : false} +
+
    +
  • + +
  • +
+
+ this.tableStore.onRowHighlight(index)} + rowClassNameGetter={this.rowClassName} + scrollToRow={tableUi.hlRowIdx} + width={tableUi.widthPx} + height={tableUi.heightPx} + {...this.props}> + {makeColumns(this.tableStore, tableUi.columns, tableUi.columnWidths, data, selectable, tableModel.selectionInfo )} +
+
); } } TablePanel.propTypes = { - tableModel : React.PropTypes.object, - showFilters : React.PropTypes.bool, - selectable : React.PropTypes.bool, - width : React.PropTypes.string, - height : React.PropTypes.string - + tableModel: React.PropTypes.object, + tbl_id: React.PropTypes.string, + tbl_ui_gid: React.PropTypes.string, + pageSize: React.PropTypes.number, + showFilters: React.PropTypes.bool, + selectable: React.PropTypes.bool, + showToolbar: React.PropTypes.bool }; +TablePanel.defaultProps = { + showFilters: true, + selectable: true, + showToolbar: true, + pageSize: 50, + tbl_ui_gid: TblUtil.uniqueTblUiGid() + +}; -export default TablePanel; diff --git a/src/firefly/js/ui/DropDownToolbarButton.jsx b/src/firefly/js/ui/DropDownToolbarButton.jsx index 9951f79314..c114dc559d 100644 --- a/src/firefly/js/ui/DropDownToolbarButton.jsx +++ b/src/firefly/js/ui/DropDownToolbarButton.jsx @@ -5,8 +5,8 @@ import React, {Component, PropTypes} from 'react'; import sCompare from 'react-addons-shallow-compare'; -import uniqueId from 'lodash/utility/uniqueId'; -import delay from 'lodash/function/delay'; +import uniqueId from 'lodash/uniqueId'; +import delay from 'lodash/delay'; import {flux} from '../Firefly.js'; import {DropDownMenuWrapper} from './DropDownMenu.jsx'; import DialogRootContainer from './DialogRootContainer.jsx'; diff --git a/src/firefly/js/ui/FormPanel.jsx b/src/firefly/js/ui/FormPanel.jsx index 9c8ab74c23..0a85ff09c5 100644 --- a/src/firefly/js/ui/FormPanel.jsx +++ b/src/firefly/js/ui/FormPanel.jsx @@ -4,7 +4,7 @@ import React from 'react'; import CompleteButton from './CompleteButton.jsx'; -import TablesCntlr from '../tables/TablesCntlr.js'; +import * as TablesCntlr from '../tables/TablesCntlr.js'; function handleFailfure() { diff --git a/src/firefly/js/ui/PointerPopup.jsx b/src/firefly/js/ui/PointerPopup.jsx index 7c742fd91f..53503cf2db 100644 --- a/src/firefly/js/ui/PointerPopup.jsx +++ b/src/firefly/js/ui/PointerPopup.jsx @@ -1,7 +1,7 @@ /*eslint "prefer-template": 0*/ import React, {Component, PropTypes} from 'react'; -import defer from 'lodash/function/defer'; +import defer from 'lodash/defer'; import ReactDOM from 'react-dom'; import sCompare from 'react-addons-shallow-compare'; import './PointerPopup.css'; diff --git a/src/firefly/js/ui/Toolbar.css b/src/firefly/js/ui/Toolbar.css new file mode 100644 index 0000000000..b069b683a3 --- /dev/null +++ b/src/firefly/js/ui/Toolbar.css @@ -0,0 +1,62 @@ +[role="toolbar"] { + position: relative; + height:26px; +} + +[role="toolbar"] .toolbar__content { + position: absolute; + top: 0; + left: 0px; + right: 0px; + background-color: #a5a5a5; + padding: 3px; +} + +[role="toolbar"] ul { + list-style: none; + padding: 0; + margin: 0; +} + +[role="toolbar"] ul[role="left"] { + width: 10%; + float: left; +} + +[role="toolbar"] ul[role="right"] { + width: 10%; + float: right; +} + +[role="toolbar"] ul[role="middle"] { + margin: auto; +} + +[role="toolbar"] li { + float: left; + margin: 0 3px; +} + +[role="toolbar"] ul[role="right"] li { + float: right; +} + +[role="toolbar"] button { + width: 50px; + height: 20px; + border: none; + font-size: 12px; + color: white; + padding: 0; + border-radius: 2px; + background: none; +} + +[role="toolbar"] button:active { + background-color: #ffffff; +} + +[role="toolbar"] button:hover { + background-color: #949494; + cursor: pointer; +} diff --git a/src/firefly/js/ui/Toolbar.jsx b/src/firefly/js/ui/Toolbar.jsx new file mode 100644 index 0000000000..d54ea9106d --- /dev/null +++ b/src/firefly/js/ui/Toolbar.jsx @@ -0,0 +1,37 @@ +/* + * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt + */ + +import React from 'react'; +import './Toolbar.css'; + + +/* +Usage: + +
    +
  • +
+
    +
  • +
  • +
  • +
  • +
+
    +
  • +
+
+ + */ +export const Toolbar = function (props) { + var {children} = props; + return ( +
+
+ {children} +
+
+ ); +}; + diff --git a/src/firefly/js/visualize/DrawLayerCntlr.js b/src/firefly/js/visualize/DrawLayerCntlr.js index d3e22a2abd..95f311ec25 100644 --- a/src/firefly/js/visualize/DrawLayerCntlr.js +++ b/src/firefly/js/visualize/DrawLayerCntlr.js @@ -7,8 +7,8 @@ import {getPlotViewIdListInGroup, getDrawLayerById} from './PlotViewUtil.js'; import VisMouseCntlr from './VisMouseCntlr.js'; import ImagePlotCntlr, {visRoot} from './ImagePlotCntlr.js'; import DrawLayerReducer from './reducer/DrawLayerReducer.js'; -import without from 'lodash/array/without'; -import union from 'lodash/array/union'; +import without from 'lodash/without'; +import union from 'lodash/union'; diff --git a/src/firefly/js/visualize/HistogramCntlr.js b/src/firefly/js/visualize/HistogramCntlr.js index 8f6b33a0a8..9bcf01acac 100644 --- a/src/firefly/js/visualize/HistogramCntlr.js +++ b/src/firefly/js/visualize/HistogramCntlr.js @@ -6,10 +6,7 @@ import {flux} from '../Firefly.js'; import {has, get, set} from 'lodash'; import {TableRequest} from '../tables/TableRequest.js'; -import LoadTable from '../tables/reducers/LoadTable.js'; -import TableUtil from '../tables/TableUtil.js'; - -import TablesCntlr from '../tables/TablesCntlr.js'; +import * as TableUtil from '../tables/TableUtil.js'; /* Possible structure of store: @@ -163,7 +160,7 @@ function fetchColData(dispatch, activeTableServerRequest, histogramParams) { req.tbl_id = activeTableServerRequest.tbl_id; - LoadTable.doFetchTable(req).then( + TableUtil.doFetchTable(req).then( (tableModel) => { if (tableModel.tableData && tableModel.tableData.data) { // if logarithmic values were requested, convert the returned exponents back diff --git a/src/firefly/js/visualize/PlotViewUtil.js b/src/firefly/js/visualize/PlotViewUtil.js index 230169c298..d006b7bc47 100644 --- a/src/firefly/js/visualize/PlotViewUtil.js +++ b/src/firefly/js/visualize/PlotViewUtil.js @@ -2,7 +2,8 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ import {getPlotGroupById} from './PlotGroup.js'; -import difference from 'lodash/array/difference'; +import {flux} from '../Firefly.js'; +import difference from 'lodash/difference'; diff --git a/src/firefly/js/visualize/TableStatsCntlr.js b/src/firefly/js/visualize/TableStatsCntlr.js index 4e1aefb6ae..2cc7a873fc 100644 --- a/src/firefly/js/visualize/TableStatsCntlr.js +++ b/src/firefly/js/visualize/TableStatsCntlr.js @@ -5,10 +5,9 @@ import {has, get, set} from 'lodash'; import ColValuesStatistics from './ColValuesStatistics.js'; import {TableRequest} from '../tables/TableRequest.js'; -import LoadTable from '../tables/reducers/LoadTable.js'; -import TableUtil from '../tables/TableUtil.js'; +import * as TableUtil from '../tables/TableUtil.js'; -import TablesCntlr from '../tables/TablesCntlr.js'; +import * as TablesCntlr from '../tables/TablesCntlr.js'; /* Possible structure of store: @@ -108,7 +107,7 @@ function reducer(state=getInitState(), action={}) { case (TablesCntlr.LOAD_TABLE) : const {tbl_id, tableMeta, request} = action.payload; if (has(state, tbl_id)) { - if (tableMeta.isFullyLoaded && !get(state, [tbl_id, 'isTblLoaded'])){ + if (TableUtil.isTableLoaded(action.payload) && !get(state, [tbl_id, 'isTblLoaded'])){ const newState = Object.assign({}, state); set(newState, tbl_id, {isTblLoaded:true}); action.sideEffect((dispatch) => fetchTblStats(dispatch,request)); @@ -159,7 +158,7 @@ function fetchTblStats(dispatch, activeTableServerRequest) { tbl_id: activeTableServerRequest.tbl_id }); - LoadTable.doFetchTable(req).then( + TableUtil.doFetchTable(req).then( (tableModel) => { if (tableModel.tableData && tableModel.tableData.data) { const colStats = tableModel.tableData.data.reduce((colstats, arow) => { diff --git a/src/firefly/js/visualize/XYPlotCntlr.js b/src/firefly/js/visualize/XYPlotCntlr.js index 3eaec3499d..59594a51c1 100644 --- a/src/firefly/js/visualize/XYPlotCntlr.js +++ b/src/firefly/js/visualize/XYPlotCntlr.js @@ -3,9 +3,9 @@ import {flux} from '../Firefly.js'; import {has, get, set} from 'lodash'; -import LoadTable from '../tables/reducers/LoadTable.js'; -//import TableUtil from '../tables/TableUtil.js'; -//import TablesCntlr from '../tables/TablesCntlr.js'; +import {doFetchTable} from '../tables/TableUtil.js'; +//import * from TableUtil from '../tables/TableUtil.js'; +//import * from TablesCntlr from '../tables/TablesCntlr.js'; const XYPLOT_DATA_KEY = 'xyplot'; @@ -155,7 +155,7 @@ function fetchPlotData(dispatch, activeTableServerRequest, xyPlotParams) { req.tbl_id = activeTableServerRequest.tbl_id; - LoadTable.doFetchTable(req).then( + doFetchTable(req).then( (tableModel) => { if (tableModel.tableData && tableModel.tableData.data) { const xyPlotData = tableModel.tableData.data; diff --git a/src/firefly/js/visualize/XYPlotTableViewPanel.jsx b/src/firefly/js/visualize/XYPlotTableViewPanel.jsx index 8353f997a8..4598a1f6eb 100644 --- a/src/firefly/js/visualize/XYPlotTableViewPanel.jsx +++ b/src/firefly/js/visualize/XYPlotTableViewPanel.jsx @@ -3,7 +3,7 @@ import React, {PropTypes} from 'react'; import {throttle} from 'lodash'; import Resizable from 'react-component-resizable'; -import TablesCntlr from '../tables/TablesCntlr.js'; +import * as TablesCntlr from '../tables/TablesCntlr.js'; import XYPlotCntlr from '../visualize/XYPlotCntlr.js'; import XYPlotOptions from '../visualize/XYPlotOptions.jsx'; import XYPlot from '../visualize/XYPlot.jsx'; diff --git a/src/firefly/js/visualize/draw/DrawerComponent.jsx b/src/firefly/js/visualize/draw/DrawerComponent.jsx index 8556081da0..39f282102f 100644 --- a/src/firefly/js/visualize/draw/DrawerComponent.jsx +++ b/src/firefly/js/visualize/draw/DrawerComponent.jsx @@ -3,8 +3,8 @@ */ import React from 'react'; -import difference from 'lodash/array/difference'; -import isEqual from 'lodash/lang/isEqual'; +import difference from 'lodash/difference'; +import isEqual from 'lodash/isEqual'; import sCompare from 'react-addons-shallow-compare'; import CanvasWrapper from './CanvasWrapper.jsx'; import TextDrawer from './TextDrawer.jsx'; diff --git a/src/firefly/js/visualize/iv/ImageViewerView.jsx b/src/firefly/js/visualize/iv/ImageViewerView.jsx index b8df57bd93..2d6718e7fd 100644 --- a/src/firefly/js/visualize/iv/ImageViewerView.jsx +++ b/src/firefly/js/visualize/iv/ImageViewerView.jsx @@ -5,7 +5,7 @@ import React, {Component,PropTypes} from 'react'; import ReactDOM from 'react-dom'; import sCompare from 'react-addons-shallow-compare'; -import debounce from 'lodash/function/debounce'; +import debounce from 'lodash/debounce'; import Resizable from 'react-component-resizable'; import {ImageViewerDecorate} from './ImageViewerDecorate.jsx'; import {dispatchZoom} from '../ImagePlotCntlr.js'; diff --git a/src/firefly/js/visualize/reducer/DrawLayerReducer.js b/src/firefly/js/visualize/reducer/DrawLayerReducer.js index 11a401c134..200f87cf97 100644 --- a/src/firefly/js/visualize/reducer/DrawLayerReducer.js +++ b/src/firefly/js/visualize/reducer/DrawLayerReducer.js @@ -6,8 +6,8 @@ import {DataTypes} from '../draw/DrawLayer.js'; import DrawLayerCntlr from '../DrawLayerCntlr.js'; import ImagePlotCntlr from '../ImagePlotCntlr.js'; -import union from 'lodash/array/union'; -import difference from 'lodash/array/difference'; +import union from 'lodash/union'; +import difference from 'lodash/difference'; diff --git a/src/firefly/test/edu/caltech/ipac/firefly/server/util/ipactable/test_data.json b/src/firefly/test/edu/caltech/ipac/firefly/server/util/ipactable/test_data.json index 39bc7d259b..8d3047307e 100644 --- a/src/firefly/test/edu/caltech/ipac/firefly/server/util/ipactable/test_data.json +++ b/src/firefly/test/edu/caltech/ipac/firefly/server/util/ipactable/test_data.json @@ -1 +1 @@ -{"request":{"RequestClass":"ServerRequest","sortInfo":"SortInfo=ASC,ra,dec","test-meta":"test-meta-value","startIdx":0,"decimate":"decimate=ra,dec,1234,0.5,,,,","pageSize":0,"id":"searchProcID","filters":"ra > 0, dec > 0"},"tbl_id":null,"tableMeta":{"EQUINOX":"'J2000'","test-meta":"test-meta-value","source":"test data","isFullyLoaded":true,"SKYAREA":"'within 500.0 arcsec of ra=10.68479 dec=+41.26906 Eq J2000 '","DataTag":"'ADS\/IRSA.Gator#2015\/0626\/151210_19090'","fixlen":"T","RowsRetrieved":"2412","SQL":"'WHERE (no constraints)","relatedCols":"","ORIGIN":"'IPAC Infrared Science Archive (IRSA), Caltech\/JPL'","DATETIME":"'2015-06-26 15:12:10'","DATABASE":"'2MASS All-Sky Point Source Catalog (PSC) (fp_psc)'","fileSize":0,"groupByCols":"","StatusFile":"'\/workspace\/TMP_GFVzWe_19090\/Gator\/irsa\/19090\/log.19090.html'"},"tableData":{"data":[["10.733387","41.214523","00h42m56.01s","41d12m52.28s","0.22","0.2","178","00425601+4112522","15.968","null","null","null","14.864","null","null","null","15.066","0.174","0.174","8.5","UUC","002","001","000","000004","2","0","n","1997-10-24","8","121.212","-21.629","0","null","null","null","null","0","null","236.330704","146.160473","null","null","null"],["10.73756","41.215744","00h42m57.01s","41d12m56.68s","0.25","0.21","81","00425701+4112566","16.313","0.165","0.165","9.7","14.736","null","null","null","14.256","null","null","null","CUU","200","100","000","050000","2","0","n","1997-10-24","8","121.215","-21.628","0","null","null","null","null","0","null","239.258783","143.324989","null","null","null"],["10.726409","41.210964","00h42m54.34s","41d12m39.47s","0.23","0.21","89","00425433+4112394","16.3","0.153","0.153","9.8","15.077","null","null","null","14.548","null","null","null","BUU","200","100","000","050000","2","0","n","1997-10-24","8","121.206","-21.632","0","null","null","null","null","0","null","237.560717","151.675477","null","null","null"],["10.739598","41.192741","00h42m57.50s","41d11m33.87s","0.2","0.17","84","00425750+4111338","16.232","0.123","0.123","10.5","15.445","0.142","0.143","9.9","15.161","0.169","0.169","7.8","BBC","222","111","000","060515","2","0","n","1997-10-24","8","121.216","-21.651","0","null","null","null","null","0","null","312.258965","151.60904","0.787","0.284","1.071"],["10.744471","41.197601","00h42m58.67s","41d11m51.36s","0.13","0.13","79","00425867+4111513","15.901","0.089","0.09","14.2","15.512","0.142","0.143","9.3","15.5","0.209","0.209","5.7","ABC","222","111","000","060506","2","0","n","1997-10-24","8","121.22","-21.646","0","null","null","null","null","0","null","303.785003","147.848202","0.389","0.012","0.401"],["10.684147","41.194","00h42m44.20s","41d11m38.40s","0.27","0.25","102","00424419+4111384","16.534","0.177","0.177","7.9","15.856","0.189","0.189","6.8","14.597","null","null","null","CCU","220","220","cc0","050500","2","0","n","1997-10-24","8","121.171","-21.648","0","null","null","null","null","0","null","270.221608","180.369332","0.678","null","null"],["10.683284","41.193066","00h42m43.99s","41d11m35.04s","0.29","0.25","87","00424398+4111350","16.398","0.155","0.155","9.0","15.957","0.21","0.21","6.2","14.526","null","null","null","BCU","220","220","cc0","060600","2","0","n","1997-10-24","8","121.17","-21.649","0","null","null","null","null","0","null","273.608782","180.854358","0.441","null","null"],["10.689488","41.18972","00h42m45.48s","41d11m22.99s","0.35","0.33","164","00424547+4111229","16.866","0.226","0.227","5.8","15.973","0.218","0.218","6.1","15.732","0.259","0.259","4.6","DDD","222","111","000","060605","2","0","n","1997-10-24","8","121.175","-21.652","0","null","null","null","null","0","null","285.907084","177.448577","0.893","0.241","1.134"],["10.692408","41.19532","00h42m46.18s","41d11m43.15s","0.28","0.25","7","00424617+4111431","16.698","0.195","0.196","6.8","15.765","0.18","0.18","7.4","14.947","null","null","null","CCU","220","110","000","060500","2","0","n","1997-10-24","8","121.178","-21.647","0","null","null","null","null","0","null","266.26399","175.554936","0.933","null","null"],["10.691879","41.191299","00h42m46.05s","41d11m28.68s","0.34","0.32","80","00424605+4111286","17.046","0.265","0.266","4.9","16.161","0.261","0.261","5.1","15.56","0.223","0.223","5.4","DDD","222","111","000","060606","2","0","n","1997-10-24","8","121.177","-21.651","0","null","null","null","null","0","null","280.596781","176.075518","0.885","0.601","1.486"],["10.699043","41.180546","00h42m47.77s","41d10m49.97s","0.08","0.07","90","00424777+4110499","13.648","0.025","0.027","113.0","13.367","0.023","0.025","67.2","13.372","0.035","0.036","40.5","AAA","222","111","000","666666","2","0","n","1997-10-24","8","121.182","-21.662","0","null","null","null","null","0","null","320.97889","173.089731","0.281","-0.005","0.276"],["10.699178","41.183144","00h42m47.80s","41d10m59.32s","0.36","0.34","180","00424780+4110593","17.027","0.241","0.242","5.0","15.803","0.174","0.174","7.1","15.078","null","null","null","DCU","220","110","c00","060300","2","0","n","1997-10-24","8","121.183","-21.659","0","null","null","null","null","0","null","311.74134","172.816453","1.224","null","null"],["10.702958","41.20282","00h42m48.71s","41d12m10.15s","0.09","0.08","90","00424870+4112101","15.364","0.056","0.057","23.3","14.701","0.067","0.068","19.7","14.693","0.102","0.102","12.0","AAA","222","111","000","060606","2","0","n","1997-10-24","8","121.186","-21.64","0","null","null","null","null","0","null","243.483459","168.339868","0.663","0.008","0.671"],["10.708817","41.194553","00h42m50.12s","41d11m40.39s","0.25","0.23","77","00425011+4111403","16.646","0.178","0.178","7.2","15.732","0.172","0.172","7.6","14.813","null","null","null","CCU","220","110","000","060500","2","0","n","1997-10-24","8","121.191","-21.648","0","null","null","null","null","0","null","276.000502","166.35987","0.914","null","null"],["10.710685","41.19548","00h42m50.56s","41d11m43.73s","0.22","0.21","119","00425056+4111437","16.027","0.096","0.097","12.6","15.654","0.155","0.156","8.2","15.255","0.173","0.173","7.2","ACC","222","111","000","060606","2","0","n","1997-10-24","8","121.192","-21.647","0","null","null","null","null","0","null","274.008477","165.167084","0.373","0.399","0.772"],["10.708745","41.198418","00h42m50.10s","41d11m54.30s","0.3","0.29","129","00425009+4111543","16.59","0.179","0.179","7.5","15.9","0.187","0.187","6.5","15.524","0.213","0.213","5.6","CCC","222","111","000","060506","2","0","n","1997-10-24","8","121.191","-21.644","0","null","null","null","null","0","null","262.450252","165.685719","0.69","0.376","1.066"]],"columns":[{"visibility":"show","name":"ra","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"dec","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"clon","sortable":true,"type":"char"},{"visibility":"show","name":"clat","sortable":true,"type":"char"},{"visibility":"show","name":"err_maj","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"err_min","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"err_ang","units":"deg","sortable":true,"type":"int"},{"visibility":"show","name":"designation","sortable":true,"type":"char"},{"visibility":"show","name":"j_m","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"j_cmsig","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"j_msigcom","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"j_snr","sortable":true,"type":"double"},{"visibility":"show","name":"h_m","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"h_cmsig","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"h_msigcom","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"h_snr","sortable":true,"type":"double"},{"visibility":"show","name":"k_m","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"k_cmsig","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"k_msigcom","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"k_snr","sortable":true,"type":"double"},{"visibility":"show","name":"ph_qual","sortable":true,"type":"char"},{"visibility":"show","name":"rd_flg","sortable":true,"type":"char"},{"visibility":"show","name":"bl_flg","sortable":true,"type":"char"},{"visibility":"show","name":"cc_flg","sortable":true,"type":"char"},{"visibility":"show","name":"ndet","sortable":true,"type":"char"},{"visibility":"show","name":"gal_contam","sortable":true,"type":"int"},{"visibility":"show","name":"mp_flg","sortable":true,"type":"int"},{"visibility":"show","name":"hemis","sortable":true,"type":"c"},{"visibility":"show","name":"xdate","sortable":true,"type":"char"},{"visibility":"show","name":"scan","sortable":true,"type":"i"},{"visibility":"show","name":"glon","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"glat","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"a","sortable":true,"type":"c"},{"visibility":"show","name":"dist_opt","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"phi_opt","units":"deg","sortable":true,"type":"int"},{"visibility":"show","name":"b_m_opt","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"vr_m_opt","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"nopt_mchs","sortable":true,"type":"int"},{"visibility":"show","name":"ext_key","sortable":true,"type":"int"},{"visibility":"show","name":"dist","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"angle","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"j_h","sortable":true,"type":"double"},{"visibility":"show","name":"h_k","sortable":true,"type":"double"},{"visibility":"show","name":"j_k","sortable":true,"type":"double"}]},"totalRows":16,"title":null,"type":"table"} \ No newline at end of file +{"request":{"RequestClass":"ServerRequest","sortInfo":"SortInfo=ASC,ra,dec","test-meta":"test-meta-value","startIdx":0,"decimate":"decimate=ra,dec,1234,0.5,,,,","pageSize":0,"id":"searchProcID","filters":"ra > 0, dec > 0"},"tbl_id":null,"tableMeta":{"EQUINOX":"'J2000'","test-meta":"test-meta-value","source":"test data","SKYAREA":"'within 500.0 arcsec of ra=10.68479 dec=+41.26906 Eq J2000 '","DataTag":"'ADS\/IRSA.Gator#2015\/0626\/151210_19090'","fixlen":"T","RowsRetrieved":"2412","SQL":"'WHERE (no constraints)","relatedCols":"","ORIGIN":"'IPAC Infrared Science Archive (IRSA), Caltech\/JPL'","DATETIME":"'2015-06-26 15:12:10'","DATABASE":"'2MASS All-Sky Point Source Catalog (PSC) (fp_psc)'","fileSize":0,"groupByCols":"","StatusFile":"'\/workspace\/TMP_GFVzWe_19090\/Gator\/irsa\/19090\/log.19090.html'"},"tableData":{"data":[["10.733387","41.214523","00h42m56.01s","41d12m52.28s","0.22","0.2","178","00425601+4112522","15.968","null","null","null","14.864","null","null","null","15.066","0.174","0.174","8.5","UUC","002","001","000","000004","2","0","n","1997-10-24","8","121.212","-21.629","0","null","null","null","null","0","null","236.330704","146.160473","null","null","null"],["10.73756","41.215744","00h42m57.01s","41d12m56.68s","0.25","0.21","81","00425701+4112566","16.313","0.165","0.165","9.7","14.736","null","null","null","14.256","null","null","null","CUU","200","100","000","050000","2","0","n","1997-10-24","8","121.215","-21.628","0","null","null","null","null","0","null","239.258783","143.324989","null","null","null"],["10.726409","41.210964","00h42m54.34s","41d12m39.47s","0.23","0.21","89","00425433+4112394","16.3","0.153","0.153","9.8","15.077","null","null","null","14.548","null","null","null","BUU","200","100","000","050000","2","0","n","1997-10-24","8","121.206","-21.632","0","null","null","null","null","0","null","237.560717","151.675477","null","null","null"],["10.739598","41.192741","00h42m57.50s","41d11m33.87s","0.2","0.17","84","00425750+4111338","16.232","0.123","0.123","10.5","15.445","0.142","0.143","9.9","15.161","0.169","0.169","7.8","BBC","222","111","000","060515","2","0","n","1997-10-24","8","121.216","-21.651","0","null","null","null","null","0","null","312.258965","151.60904","0.787","0.284","1.071"],["10.744471","41.197601","00h42m58.67s","41d11m51.36s","0.13","0.13","79","00425867+4111513","15.901","0.089","0.09","14.2","15.512","0.142","0.143","9.3","15.5","0.209","0.209","5.7","ABC","222","111","000","060506","2","0","n","1997-10-24","8","121.22","-21.646","0","null","null","null","null","0","null","303.785003","147.848202","0.389","0.012","0.401"],["10.684147","41.194","00h42m44.20s","41d11m38.40s","0.27","0.25","102","00424419+4111384","16.534","0.177","0.177","7.9","15.856","0.189","0.189","6.8","14.597","null","null","null","CCU","220","220","cc0","050500","2","0","n","1997-10-24","8","121.171","-21.648","0","null","null","null","null","0","null","270.221608","180.369332","0.678","null","null"],["10.683284","41.193066","00h42m43.99s","41d11m35.04s","0.29","0.25","87","00424398+4111350","16.398","0.155","0.155","9.0","15.957","0.21","0.21","6.2","14.526","null","null","null","BCU","220","220","cc0","060600","2","0","n","1997-10-24","8","121.17","-21.649","0","null","null","null","null","0","null","273.608782","180.854358","0.441","null","null"],["10.689488","41.18972","00h42m45.48s","41d11m22.99s","0.35","0.33","164","00424547+4111229","16.866","0.226","0.227","5.8","15.973","0.218","0.218","6.1","15.732","0.259","0.259","4.6","DDD","222","111","000","060605","2","0","n","1997-10-24","8","121.175","-21.652","0","null","null","null","null","0","null","285.907084","177.448577","0.893","0.241","1.134"],["10.692408","41.19532","00h42m46.18s","41d11m43.15s","0.28","0.25","7","00424617+4111431","16.698","0.195","0.196","6.8","15.765","0.18","0.18","7.4","14.947","null","null","null","CCU","220","110","000","060500","2","0","n","1997-10-24","8","121.178","-21.647","0","null","null","null","null","0","null","266.26399","175.554936","0.933","null","null"],["10.691879","41.191299","00h42m46.05s","41d11m28.68s","0.34","0.32","80","00424605+4111286","17.046","0.265","0.266","4.9","16.161","0.261","0.261","5.1","15.56","0.223","0.223","5.4","DDD","222","111","000","060606","2","0","n","1997-10-24","8","121.177","-21.651","0","null","null","null","null","0","null","280.596781","176.075518","0.885","0.601","1.486"],["10.699043","41.180546","00h42m47.77s","41d10m49.97s","0.08","0.07","90","00424777+4110499","13.648","0.025","0.027","113.0","13.367","0.023","0.025","67.2","13.372","0.035","0.036","40.5","AAA","222","111","000","666666","2","0","n","1997-10-24","8","121.182","-21.662","0","null","null","null","null","0","null","320.97889","173.089731","0.281","-0.005","0.276"],["10.699178","41.183144","00h42m47.80s","41d10m59.32s","0.36","0.34","180","00424780+4110593","17.027","0.241","0.242","5.0","15.803","0.174","0.174","7.1","15.078","null","null","null","DCU","220","110","c00","060300","2","0","n","1997-10-24","8","121.183","-21.659","0","null","null","null","null","0","null","311.74134","172.816453","1.224","null","null"],["10.702958","41.20282","00h42m48.71s","41d12m10.15s","0.09","0.08","90","00424870+4112101","15.364","0.056","0.057","23.3","14.701","0.067","0.068","19.7","14.693","0.102","0.102","12.0","AAA","222","111","000","060606","2","0","n","1997-10-24","8","121.186","-21.64","0","null","null","null","null","0","null","243.483459","168.339868","0.663","0.008","0.671"],["10.708817","41.194553","00h42m50.12s","41d11m40.39s","0.25","0.23","77","00425011+4111403","16.646","0.178","0.178","7.2","15.732","0.172","0.172","7.6","14.813","null","null","null","CCU","220","110","000","060500","2","0","n","1997-10-24","8","121.191","-21.648","0","null","null","null","null","0","null","276.000502","166.35987","0.914","null","null"],["10.710685","41.19548","00h42m50.56s","41d11m43.73s","0.22","0.21","119","00425056+4111437","16.027","0.096","0.097","12.6","15.654","0.155","0.156","8.2","15.255","0.173","0.173","7.2","ACC","222","111","000","060606","2","0","n","1997-10-24","8","121.192","-21.647","0","null","null","null","null","0","null","274.008477","165.167084","0.373","0.399","0.772"],["10.708745","41.198418","00h42m50.10s","41d11m54.30s","0.3","0.29","129","00425009+4111543","16.59","0.179","0.179","7.5","15.9","0.187","0.187","6.5","15.524","0.213","0.213","5.6","CCC","222","111","000","060506","2","0","n","1997-10-24","8","121.191","-21.644","0","null","null","null","null","0","null","262.450252","165.685719","0.69","0.376","1.066"]],"columns":[{"visibility":"show","name":"ra","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"dec","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"clon","sortable":true,"type":"char"},{"visibility":"show","name":"clat","sortable":true,"type":"char"},{"visibility":"show","name":"err_maj","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"err_min","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"err_ang","units":"deg","sortable":true,"type":"int"},{"visibility":"show","name":"designation","sortable":true,"type":"char"},{"visibility":"show","name":"j_m","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"j_cmsig","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"j_msigcom","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"j_snr","sortable":true,"type":"double"},{"visibility":"show","name":"h_m","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"h_cmsig","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"h_msigcom","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"h_snr","sortable":true,"type":"double"},{"visibility":"show","name":"k_m","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"k_cmsig","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"k_msigcom","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"k_snr","sortable":true,"type":"double"},{"visibility":"show","name":"ph_qual","sortable":true,"type":"char"},{"visibility":"show","name":"rd_flg","sortable":true,"type":"char"},{"visibility":"show","name":"bl_flg","sortable":true,"type":"char"},{"visibility":"show","name":"cc_flg","sortable":true,"type":"char"},{"visibility":"show","name":"ndet","sortable":true,"type":"char"},{"visibility":"show","name":"gal_contam","sortable":true,"type":"int"},{"visibility":"show","name":"mp_flg","sortable":true,"type":"int"},{"visibility":"show","name":"hemis","sortable":true,"type":"c"},{"visibility":"show","name":"xdate","sortable":true,"type":"char"},{"visibility":"show","name":"scan","sortable":true,"type":"i"},{"visibility":"show","name":"glon","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"glat","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"a","sortable":true,"type":"c"},{"visibility":"show","name":"dist_opt","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"phi_opt","units":"deg","sortable":true,"type":"int"},{"visibility":"show","name":"b_m_opt","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"vr_m_opt","units":"mag","sortable":true,"type":"double"},{"visibility":"show","name":"nopt_mchs","sortable":true,"type":"int"},{"visibility":"show","name":"ext_key","sortable":true,"type":"int"},{"visibility":"show","name":"dist","units":"arcsec","sortable":true,"type":"double"},{"visibility":"show","name":"angle","units":"deg","sortable":true,"type":"double"},{"visibility":"show","name":"j_h","sortable":true,"type":"double"},{"visibility":"show","name":"h_k","sortable":true,"type":"double"},{"visibility":"show","name":"j_k","sortable":true,"type":"double"}]},"totalRows":16,"title":null,"type":"table"} \ No newline at end of file From 70f3feac52d728ebe4e820d15c1758c4134ab53b Mon Sep 17 00:00:00 2001 From: loi Date: Wed, 10 Feb 2016 10:42:29 -0800 Subject: [PATCH 2/2] changes in response to reviewer's comments. --- src/firefly/js/tables/TableUtil.js | 10 +++++----- src/firefly/js/tables/ui/TablePanel.jsx | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/firefly/js/tables/TableUtil.js b/src/firefly/js/tables/TableUtil.js index 50dd09228f..178da82458 100644 --- a/src/firefly/js/tables/TableUtil.js +++ b/src/firefly/js/tables/TableUtil.js @@ -56,10 +56,10 @@ export function doValidate(type, action) { } var {request} = action.payload; if (type === TblCntlr.FETCH_TABLE ) { - if (isEmpty(request.id)) { + if (request.id) { error(action, 'Required "id" field is missing.'); } - if (isEmpty(request.tbl_id)) { + if (request.tbl_id) { error(action, 'Required "tbl_id" field is missing.'); } } else if(type === TblCntlr.TBL_HIGHLIGHT_ROW) { @@ -149,8 +149,8 @@ export function transform(tableModel) { export function smartMerge(target, source) { if (!target) return source; - if ( source && typeof(source)=='object') { - if(source instanceof Array) { + if ( source && typeof(source)==='object') { + if(Array.isArray(source)) { let aryChanges = []; source.forEach( (v, idx) => { const nval = smartMerge(target[idx], source[idx]); @@ -175,7 +175,7 @@ export function smartMerge(target, source) { return (isEmpty(objChanges)) ? target : Object.assign({}, target, objChanges); } } else { - return (target == source) ? target : source; + return (target === source) ? target : source; } } diff --git a/src/firefly/js/tables/ui/TablePanel.jsx b/src/firefly/js/tables/ui/TablePanel.jsx index 775ad20e77..e2ffeb6e82 100644 --- a/src/firefly/js/tables/ui/TablePanel.jsx +++ b/src/firefly/js/tables/ui/TablePanel.jsx @@ -21,9 +21,9 @@ import LOADING from 'html/images/gxt/loading.gif'; const {Table, Column, Cell} = FixedDataTable; -const TextCell = ({rowIndex, data, col, ...rest}) => { +const TextCell = ({rowIndex, data, col}) => { return ( - + {get(data, [rowIndex, col],'undef')} );