-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #139 from jpmorganchase/hypergrid3
Hypergrid 3
- Loading branch information
Showing
16 changed files
with
725 additions
and
1,445 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
88 changes: 88 additions & 0 deletions
88
packages/perspective-viewer-hypergrid/src/js/CachedRendererPlugin.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
/****************************************************************************** | ||
* | ||
* Copyright (c) 2017, the Perspective Authors. | ||
* | ||
* This file is part of the Perspective library, distributed under the terms of | ||
* the Apache License 2.0. The full license can be found in the LICENSE file. | ||
* | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const rectangular = require('rectangular'); | ||
|
||
const Range = require('./Range'); | ||
|
||
function getSubrects() { | ||
let range = Range.estimate(this.grid); | ||
range.end_row = Math.min(range.end_row, this.grid.behavior.dataModel.getRowCount()); | ||
if (!this.dataWindow) { | ||
return [] | ||
} | ||
var dw = this.dataWindow; | ||
var rect = this.grid.newRectangle(dw.left, Math.min(dw.top, range.start_row), dw.width, Math.max(dw.height, range.end_row)); // convert from InclusiveRect | ||
return [rect]; | ||
} | ||
|
||
function CachedRendererPlugin(grid) { | ||
|
||
async function update_cache() { | ||
return await new Promise(resolve => { | ||
const rects = getSubrects.call(grid.renderer) | ||
grid.behavior.dataModel.fetchData(rects, val => resolve(!val)); | ||
}); | ||
} | ||
|
||
grid.canvas._paintNow = grid.canvas.paintNow; | ||
|
||
grid.canvas.resize = async function() { | ||
var box = this.size = this.getDivBoundingClientRect(), | ||
width = this.width = Math.round(box.width), | ||
height = this.height = Math.round(box.height), | ||
ratio = 1, | ||
isHIDPI = window.devicePixelRatio && this.component.properties.useHiDPI; | ||
|
||
if (isHIDPI) { | ||
var devicePixelRatio = window.devicePixelRatio || 1; | ||
var backingStoreRatio = this.gc.webkitBackingStorePixelRatio || | ||
this.gc.mozBackingStorePixelRatio || | ||
this.gc.msBackingStorePixelRatio || | ||
this.gc.oBackingStorePixelRatio || | ||
this.gc.backingStorePixelRatio || 1; | ||
|
||
ratio = devicePixelRatio / backingStoreRatio; | ||
} | ||
|
||
this.bounds = new rectangular.Rectangle(0, 0, width, height); | ||
this.component.setBounds(this.bounds); | ||
this.resizeNotification(); | ||
|
||
let render = await update_cache(); | ||
|
||
if (render) { | ||
this.buffer.width = this.canvas.width = width * ratio; | ||
this.buffer.height = this.canvas.height = height * ratio; | ||
|
||
this.canvas.style.width = this.buffer.style.width = width + 'px'; | ||
this.canvas.style.height = this.buffer.style.height = height + 'px'; | ||
|
||
this.bc.scale(ratio, ratio); | ||
if (isHIDPI && !this.component.properties.useBitBlit) { | ||
this.gc.scale(ratio, ratio); | ||
} | ||
|
||
grid.canvas._paintNow(); | ||
} | ||
}; | ||
|
||
grid.canvas.paintNow = async function() { | ||
let render = await update_cache(); | ||
if (render) { | ||
grid.canvas._paintNow(); | ||
} | ||
}; | ||
|
||
} | ||
|
||
|
||
module.exports = CachedRendererPlugin; |
105 changes: 105 additions & 0 deletions
105
packages/perspective-viewer-hypergrid/src/js/PerspectiveDataModel.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/****************************************************************************** | ||
* | ||
* Copyright (c) 2017, the Perspective Authors. | ||
* | ||
* This file is part of the Perspective library, distributed under the terms of | ||
* the Apache License 2.0. The full license can be found in the LICENSE file. | ||
* | ||
*/ | ||
|
||
const Range = require('./Range'); | ||
|
||
const TREE_COLUMN_INDEX = require('fin-hypergrid/src/behaviors/Behavior').prototype.treeColumnIndex; | ||
|
||
module.exports = require('datasaur-local').extend('PerspectiveDataModel', { | ||
|
||
isTreeCol: function (x) { | ||
return x === TREE_COLUMN_INDEX && this.isTree(); | ||
}, | ||
|
||
getValue: function (x, y) { | ||
var row = this.data[y]; | ||
return row ? row[x] : null; | ||
}, | ||
|
||
getRowCount: function () { | ||
return this._nrows || 0; | ||
}, | ||
|
||
setRowCount: function (count) { | ||
this._nrows = count || 0; | ||
}, | ||
|
||
isTree: function () { | ||
return this._isTree; | ||
}, | ||
|
||
setIsTree: function (isTree) { | ||
this._isTree = isTree; | ||
}, | ||
|
||
|
||
fetchData: function (rectangles, resolve) { | ||
if (!rectangles.find(uncachedRow, this)) { | ||
resolve(false); | ||
return; | ||
} | ||
|
||
this.data.length = 0; | ||
|
||
const promises = rectangles.map( | ||
rect => this.pspFetch(Range.create(rect.origin.y, rect.corner.y + 2)) | ||
); | ||
|
||
Promise.all(promises).then(() => { | ||
resolve(false); | ||
}).catch(() => { | ||
resolve(true); | ||
}); | ||
}, | ||
|
||
getCell: function (config, rendererName) { | ||
var nextRow, depthDelta; | ||
if (config.isUserDataArea) { | ||
cellStyle.call(this, config, rendererName); | ||
} else if (config.dataCell.x === TREE_COLUMN_INDEX) { | ||
nextRow = this.getRow(config.dataCell.y + 1); | ||
depthDelta = nextRow ? config.value.rowPath.length - nextRow[TREE_COLUMN_INDEX].rowPath.length : -1; | ||
config.last = depthDelta !== 0; | ||
config.expanded = depthDelta < 0; | ||
config._type = this.schema[-1].type[config.value.rowPath.length - 2]; | ||
} | ||
return config.grid.cellRenderers.get(rendererName); | ||
}, | ||
|
||
pspFetch: async function() {} | ||
}); | ||
|
||
function uncachedRow(rect) { | ||
for (var r = rect.origin.y, R = Math.min(rect.corner.y + 2, this.getRowCount()); r < R; ++r) { | ||
if (!this.data[r]) { | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
function cellStyle(gridCellConfig) { | ||
if (gridCellConfig.value === null || gridCellConfig.value === undefined) { | ||
gridCellConfig.value = '-'; | ||
} else { | ||
const type = this.schema[gridCellConfig.dataCell.x].type; | ||
if (['number', 'float', 'integer'].indexOf(type) > -1) { | ||
if (gridCellConfig.value === 0) { | ||
gridCellConfig.value = type === 'float' ? '0.00' : '0'; | ||
} else if (isNaN(gridCellConfig.value)) { | ||
gridCellConfig.value = '-'; | ||
} else { | ||
gridCellConfig.color = gridCellConfig.value >= 0 | ||
? (gridCellConfig.columnHeaderBackgroundNumberPositive || 'rgb(160,207,255)') | ||
: (gridCellConfig.columnHeaderBackgroundNumberNegative || 'rgb(255,136,136)'); | ||
} | ||
} else if (type === 'boolean') { | ||
gridCellConfig.value = String(gridCellConfig.value); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/****************************************************************************** | ||
* | ||
* Copyright (c) 2017, the Perspective Authors. | ||
* | ||
* This file is part of the Perspective library, distributed under the terms of | ||
* the Apache License 2.0. The full license can be found in the LICENSE file. | ||
* | ||
*/ | ||
|
||
/** | ||
* @constructor | ||
* @returns | ||
*/ | ||
function Range() {} | ||
|
||
/** | ||
* Creates a new {@link Range} and sets it's start and end rows. | ||
* @param start_row | ||
* @param end_row | ||
*/ | ||
Range.create = function(start_row, end_row) { | ||
var range = new Range; | ||
range.reset(start_row, end_row); | ||
return range; | ||
}; | ||
|
||
/** | ||
* Creates a new {@link Range} and sets it to the estimated range of the given `grid` + `padding`. | ||
* @param grid | ||
* @param padding | ||
*/ | ||
Range.estimate = function(grid) { | ||
var range = new Range; | ||
range.estimatedGridRange(grid); | ||
return range; | ||
}; | ||
|
||
/** | ||
* How many rows to pad the cache by. | ||
* @todo This should really be calculated from the parameters of the theme. | ||
*/ | ||
Range.padding = 5; | ||
|
||
/** | ||
* @constructor | ||
* @param {number} start_row | ||
* @param {number} end_row | ||
* @returns | ||
*/ | ||
Range.prototype.reset = function(start_row, end_row) { | ||
this.start_row = start_row; | ||
this.end_row = end_row; | ||
}; | ||
|
||
/** | ||
* Estimate the visible range of rows of a Hypergrid based on: | ||
* * Index of top visible row; and | ||
* * Number of rows that can fit in the canvas based at its current height | ||
* | ||
* This calculation assumes all rows have the default row height. | ||
* If any of the rows are taller than the default, this will be an overestimate, which is fine. | ||
* (If on the other hand the average row height of the visible rows is _less_ than the default, | ||
* this will be an underestimate, which would be a problem.) | ||
* @param {Hypergrid} grid | ||
* @returns | ||
*/ | ||
Range.prototype.estimatedGridRange = function(grid) { | ||
var start_row = grid.renderer.getScrollTop(), | ||
row_count = Math.ceil(grid.canvas.height / grid.properties.defaultRowHeight), | ||
end_row = start_row + row_count + Range.padding; | ||
|
||
this.reset(start_row, end_row); | ||
}; | ||
|
||
/** | ||
* @returns {boolean} Range is invalid. | ||
*/ | ||
Range.prototype.isInvalid = function() { | ||
return Number.isNaN(this.start_row) || Number.isNaN(this.end_row); | ||
}; | ||
|
||
/** | ||
* The estimated range is within the given range. | ||
* @param {number[]} [range] | ||
* @returns {boolean} | ||
*/ | ||
Range.prototype.within = function(range) { | ||
return range instanceof Range && | ||
range.start_row <= this.start_row && | ||
this.end_row <= range.end_row; | ||
}; | ||
|
||
/** | ||
* The estimated range contains the given range. | ||
* @param {number[]} [range] | ||
* @returns {boolean} | ||
*/ | ||
Range.prototype.contains = function(range) { | ||
return range instanceof Range && range.within(this); | ||
}; | ||
|
||
|
||
module.exports = Range; |
Oops, something went wrong.