fin-hypergrid
Advanced tools
Comparing version 1.0.8 to 1.0.9
'use strict'; | ||
// NOTE: gulpfile.js's 'add-ons' task copies this file, altering the final line, to /demo/build/add-ons/, along with a minified version. Both files are eventually deployed to http://openfin.github.io/fin-hypergrid/add-ons/. | ||
/** | ||
@@ -4,0 +6,0 @@ * @classdesc This is a simple helper class to set up the aggregations-view data source in the context of a hypergrid. |
'use strict'; | ||
// NOTE: gulpfile.js's 'add-ons' task copies this file, altering the final line, to /demo/build/add-ons/, along with a minified version. Both files are eventually deployed to http://openfin.github.io/fin-hypergrid/add-ons/. | ||
/** | ||
@@ -193,4 +195,2 @@ * Methods for programmatic drill-down manipulation. | ||
//TODO: THIS IMPLEMENTATION IS SPECIFIC TO TREEVIEW it should be moved to add-ons/tree-view.js | ||
/** | ||
@@ -197,0 +197,0 @@ * @summary Expand nested drill-downs containing this row. |
/* globals CustomEvent */ | ||
// NOTE: gulpfile.js's 'add-ons' task copies this file, altering the final line, to /demo/build/add-ons/, along with a minified version. Both files are eventually deployed to http://openfin.github.io/fin-hypergrid/add-ons/. | ||
'use strict'; | ||
@@ -4,0 +6,0 @@ |
@@ -1,175 +0,374 @@ | ||
'use strict'; | ||
/* eslint-env browser */ | ||
var groupedHeader = { | ||
// NOTE: gulpfile.js's 'add-ons' task copies this file, altering the final line, to /demo/build/add-ons/, along with a minified version. Both files are eventually deployed to http://openfin.github.io/fin-hypergrid/add-ons/. | ||
mixInTo: function ColumnGrouper(grid, options) { | ||
options = options || {}; | ||
'use strict'; | ||
// 1. Create a special cell renderer to be used for the grouped header cells. | ||
var SimpleCell = Object.getPrototypeOf(grid.cellRenderers.get('SimpleCell')).constructor, | ||
GroupedHeader = SimpleCell.extend('GroupedHeader', { | ||
paint: this.paintHeaderGroups, | ||
delimiter: '|', | ||
groupColor: 'rgb(144, 144, 144)', | ||
paintBackground: this.drawProportionalBottomBorder, | ||
gradientStops: [ | ||
[0, 'rgba(144, 144, 144, 0)'], | ||
[1, 'rgba(144, 144, 144, .35)'] | ||
] | ||
}), | ||
rendererSingleton = grid.cellRenderers.add(GroupedHeader); | ||
var prototypeAdditions = { | ||
/** | ||
* Override of SimpleCell's {@link SimpleCell#paint|paint} method. | ||
* @type {function} | ||
* @memberOf groupedHeader.mixInTo~GroupedHeader.prototype | ||
*/ | ||
paint: paintHeaderGroups, | ||
rendererSingleton.groups = []; | ||
// Remaining members are exclusive to `GroupedHeader` (not overrides) | ||
if (options.delimiter) { | ||
rendererSingleton.delimiter = grid.behavior.dataModel.groupHeaderDelimiter = options.delimiter; | ||
} | ||
/** | ||
* Character used in header strings to concatenate group label(s) with actual header. | ||
* @type {string} | ||
* @default '|' (vertical bar) | ||
* @memberOf groupedHeader.mixInTo~GroupedHeader.prototype | ||
*/ | ||
delimiter: '|', | ||
if (options.groupColor) { | ||
rendererSingleton.groupColor = options.groupColor; | ||
} | ||
/** | ||
* Group label color. | ||
* @type {string} | ||
* @default 'rgb(144, 144, 144)' | ||
* @memberOf groupedHeader.mixInTo~GroupedHeader.prototype | ||
*/ | ||
groupColor: 'rgb(144, 144, 144)', | ||
if (options.paintBackground) { | ||
rendererSingleton.paintBackground = options.paintBackground; | ||
if (options.paintBackground === this.drawLinearGradient && options.gradientStops) { | ||
this.gradientStops = options.gradientStops; | ||
} | ||
} | ||
/** | ||
* Background renderer. | ||
* @type {function} | ||
* @default {@link groupedHeader.drawProportionalBottomBorder} | ||
* @memberOf groupedHeader.mixInTo~GroupedHeader.prototype | ||
*/ | ||
paintBackground: drawProportionalBottomBorder, | ||
// 2. Add a `setHeaders` method to the behavior. | ||
grid.behavior.setHeaders = this.setHeaders; | ||
}, | ||
/** | ||
* List of gradient stops that define the gradient. | ||
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient/addColorStop|addColorStop} for more info. | ||
* @type {gradientStop[]} | ||
* @default [ [0, 'rgba(144, 144, 144, 0)'], [1, 'rgba(144, 144, 144, .35)'] ] | ||
* @memberOf groupedHeader.mixInTo~GroupedHeader.prototype | ||
*/ | ||
gradientStops: [ | ||
[0, 'rgba(144, 144, 144, 0)'], | ||
[1, 'rgba(144, 144, 144, .35)'] | ||
] | ||
}; | ||
setHeaders: function(headers) { | ||
var delimiter = this.grid.cellRenderers.get('GroupedHeader').delimiter; | ||
/** | ||
* @summary Mix in the code necessary to support grouped column headers. | ||
* @desc Performs the following mix ins: | ||
* 1. Creates a new renderer and adds it to the grid. | ||
* 2. Sets the data model's {@link dataModel#groupHeaderDelimiter|groupHeaderDelimiter} property, which tells the data model to prepend the up and down sort arrows to the actual column header part of the header string rather than the start of the header string (which is the highest-order group label). | ||
* @function | ||
* @param {Hypergrid} grid - Your instantiated grid object. | ||
* @param {object} options - Overrides for the {@link groupedHeader.mixInTo~GroupedHeader|GroupedHeader} cell renderer's _own_ members. | ||
* @memberOf groupedHeader | ||
*/ | ||
function mixInTo(grid, options) { | ||
Object.getPrototypeOf(this).setHeaders.call(this, headers); // see behavior/JSON.js:setHeaders for more info | ||
var SimpleCell = Object.getPrototypeOf(grid.cellRenderers.get('SimpleCell')).constructor; | ||
// Discover the deepest group level from all the header strings | ||
var levels = this.allColumns.reduce(function(max, column) { | ||
return Math.max(column.header.split(delimiter).length, max); | ||
}, 0); | ||
// Increase the height of header row to accommodate all group header levels | ||
this.grid.setState({ | ||
rowHeights: { | ||
0: levels * 4 / 3 * this.grid.behavior.getDefaultRowHeight() | ||
} | ||
}); | ||
}, | ||
/** | ||
* @this {GroupHeader} | ||
* @param gc | ||
* @param config | ||
* @extends SimpleCell | ||
* @constructor | ||
*/ | ||
paintHeaderGroups: function(gc, config) { | ||
var paint = this.super.paint; | ||
var GroupedHeader = SimpleCell.extend('GroupedHeader', prototypeAdditions); | ||
if (config.y === 0 && config.x >= 0) { // if a header cell... | ||
var groups = this.groups, | ||
values = config.value.split(this.delimiter), // each group header including column header | ||
groupCount = values.length - 1, // group header levels above column header | ||
group; | ||
// 1. Create a special cell renderer to be used for the grouped header cells. | ||
var renderer = grid.cellRenderers.add(GroupedHeader); | ||
if (groupCount === 0 || config.x === 0) { // no group headers OR first column | ||
groups.length = 0; // start out with no groups defined | ||
// 2. Set instance variables from `options` object, overriding values in above prototype | ||
if (options) { | ||
for (var key in Object.keys(options)) { | ||
if (options.hasOwnProperty(options)) { | ||
renderer[key] = options[key]; | ||
} | ||
} | ||
} | ||
if (groupCount) { // has group headers | ||
var bounds = config.bounds, | ||
// 2. Extend the behavior's `setHeaders` method. | ||
grid.behavior.setHeaders = this.setHeaders; // This override calls the superclass's implementation | ||
// save bounds for final column header render | ||
boundsLeft = bounds.x, | ||
boundsWidth = bounds.width, | ||
// 3. Set the datamodel's `groupHeaderDelimiter` property | ||
grid.behavior.dataModel.groupHeaderDelimiter = renderer.delimiter; | ||
} | ||
// save cosmetic properties for final column header render | ||
isColumnHovered = config.isColumnHovered, | ||
isSelected = config.isSelected, | ||
font = config.font, | ||
color = config.color; | ||
/** | ||
* @summary Set the headers _and_ set the header row height. | ||
* @desc Convenience function to: | ||
* 1. Call the underlying {@link behaviors/JSON#setHeaders|setHeaders} method. | ||
* 2. Set the header row height based on the maximum group depth. | ||
* @this {Behavior} | ||
* @param {string[]|object} headers - The header labels. One of: | ||
* * _If an array:_ Must contain all headers in column order. | ||
* * _If a hash:_ May contain any headers, keyed by field name, in any order. | ||
* @memberOf groupedHeader | ||
*/ | ||
function setHeaders(headers) { | ||
var delimiter = this.grid.cellRenderers.get('GroupedHeader').delimiter; | ||
// height of each level is the same, 1/levels of total height | ||
bounds.height /= values.length; | ||
// Call the original implementation to set the headers | ||
Object.getPrototypeOf(this).setHeaders.call(this, headers); | ||
for (var g = 0, y = bounds.y; g < groupCount; g++, y += bounds.height) { | ||
if (!groups[g] || values[g] !== groups[g].value) { | ||
// Level has changed so reset left position (group[g] as on the renderer and persists between calls) | ||
group = groups[g] = groups[g] || {}; | ||
group.value = values[g]; | ||
group.left = boundsLeft; | ||
group.width = boundsWidth; | ||
} else { | ||
// Continuation of same group level, so just repaint but with increased width | ||
group = groups[g]; | ||
group.width += boundsWidth; | ||
} | ||
// Discover the deepest group level from all the header strings | ||
var levels = this.allColumns.reduce(function(max, column) { | ||
return Math.max(column.header.split(delimiter).length, max); | ||
}, 0); | ||
bounds.x = group.left; | ||
bounds.y = y; | ||
bounds.width = group.width; | ||
config.value = group.value; | ||
// Increase the height of header row to accommodate all the deepest group header level | ||
this.grid.setState({ | ||
rowHeights: { | ||
0: levels * 4 / 3 * this.grid.behavior.getDefaultRowHeight() | ||
} | ||
}); | ||
} | ||
// Suppress hover and selection effects for group headers | ||
config.isColumnHovered = config.isSelected = false; | ||
/** | ||
* @this {GroupHeader} | ||
* @param {CanvasRenderingContext2D} gc | ||
* @param {object} config | ||
* @memberOf groupedHeader | ||
*/ | ||
function paintHeaderGroups(gc, config) { | ||
var paint = this.super.paint; | ||
// Make group headers bold & grey | ||
config.font = 'bold ' + font; | ||
config.color = this.groupColor; | ||
if (config.y === 0 && config.x >= 0) { // if a header cell... | ||
var values = config.value.split(this.delimiter), // each group header including column header | ||
groupCount = values.length - 1; // group header levels above column header | ||
// Render the higher-order group labels | ||
paint.call(this, gc, config); | ||
if (groupCount === 0 || config.x === 0) { // no group headers OR first column | ||
this.groups = []; // start out with no groups defined | ||
} | ||
// draw a border of some kind | ||
this.bounds = bounds; | ||
this.groupIndex = g; | ||
this.groupCount = groupCount; | ||
this.paintBackground(gc); | ||
if (groupCount) { // has group headers | ||
var group, | ||
groups = this.groups, | ||
bounds = config.bounds, | ||
// save bounds for final column header render | ||
boundsLeft = bounds.x, | ||
boundsWidth = bounds.width, | ||
// save cosmetic properties for final column header render | ||
isColumnHovered = config.isColumnHovered, | ||
isSelected = config.isSelected, | ||
font = config.font, | ||
color = config.color; | ||
// height of each level is the same, 1/levels of total height | ||
bounds.height /= values.length; | ||
for (var g = 0, y = bounds.y; g < groupCount; g++, y += bounds.height) { | ||
if (!groups[g] || values[g] !== groups[g].value) { | ||
// Level has changed so reset left position (group[g] as on the renderer and persists between calls) | ||
group = groups[g] = groups[g] || {}; | ||
group.value = values[g]; | ||
group.left = boundsLeft; | ||
group.width = boundsWidth; | ||
} else { | ||
// Continuation of same group level, so just repaint but with increased width | ||
group = groups[g]; | ||
group.width += boundsWidth; | ||
} | ||
// restore bounds for final column header render (although y and height have been altered) | ||
bounds.x = boundsLeft; | ||
bounds.x = group.left; | ||
bounds.y = y; | ||
bounds.width = boundsWidth; | ||
config.value = values[g]; // low-order header | ||
bounds.width = group.width; | ||
config.value = group.value; | ||
// restore cosmetic values for hover and selection | ||
config.isColumnHovered = isColumnHovered; | ||
config.isSelected = isSelected; | ||
// Suppress hover and selection effects for group headers | ||
config.isColumnHovered = config.isSelected = false; | ||
// restore font and color which were set to bold grey above for group headers | ||
config.font = font; | ||
config.color = color; | ||
// Make group headers bold & grey | ||
config.font = 'bold ' + font; | ||
config.color = this.groupColor; | ||
// Render the higher-order group labels | ||
paint.call(this, gc, config); | ||
// Decorate the group label's background | ||
this.bounds = bounds; | ||
this.groupIndex = g; | ||
this.groupCount = groupCount; | ||
this.paintBackground(gc); | ||
} | ||
// restore bounds for final column header render (although y and height have been altered) | ||
bounds.x = boundsLeft; | ||
bounds.y = y; | ||
bounds.width = boundsWidth; | ||
config.value = values[g]; // low-order header | ||
// restore cosmetic values for hover and selection | ||
config.isColumnHovered = isColumnHovered; | ||
config.isSelected = isSelected; | ||
// restore font and color which were set to bold grey above for group headers | ||
config.font = font; | ||
config.color = color; | ||
} | ||
} | ||
// Render the low-order column header | ||
paint.call(this, gc, config); | ||
}, | ||
// Render the low-order column header | ||
paint.call(this, gc, config); | ||
} | ||
drawProportionalBottomBorder: function(gc) { | ||
var bounds = this.bounds; | ||
var thickness = this.groupCount - this.groupIndex; // high-order group gets thickest border | ||
gc.beginPath(); | ||
gc.strokeStyle = this.groupColor; | ||
gc.lineWidth = thickness; | ||
gc.moveTo(bounds.x + 3, bounds.y + bounds.height - thickness); | ||
gc.lineTo(bounds.x + bounds.width - 2, bounds.y + bounds.height - thickness); | ||
gc.stroke(); | ||
gc.closePath(); | ||
}, | ||
drawLinearGradient: function(gc) { | ||
var bounds = this.bounds; | ||
var grad = gc.createLinearGradient(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height); | ||
this.gradientStops.forEach(function(stop) { | ||
grad.addColorStop.apply(grad, stop); | ||
}); | ||
gc.fillStyle = grad; | ||
gc.fillRect(bounds.x + 2, bounds.y, bounds.width - 3, bounds.height); | ||
/** | ||
* @summary Draw underscore under group label. | ||
* @desc Supply a reference to this function in the `paintBackground` option in your {@link groupedHeader.mixInTo} call. This function is not called directly. | ||
* @this {GroupHeader} | ||
* @param {CanvasRenderingContext2D} gc | ||
* @memberOf groupedHeader | ||
*/ | ||
function drawProportionalBottomBorder(gc) { | ||
var bounds = this.bounds; | ||
var thickness = this.groupCount - this.groupIndex; // high-order group gets thickest border | ||
gc.beginPath(); | ||
gc.strokeStyle = this.groupColor; | ||
gc.lineWidth = thickness; | ||
gc.moveTo(bounds.x + 3, bounds.y + bounds.height - thickness); | ||
gc.lineTo(bounds.x + bounds.width - 2, bounds.y + bounds.height - thickness); | ||
gc.stroke(); | ||
gc.closePath(); | ||
} | ||
} | ||
/** | ||
* @summary Draw vertical gradient behind group label. | ||
* @desc Supply a reference to this function in the `paintBackground` option in your {@link groupedHeader.mixInTo} call. This function is not called directly. | ||
* @this {GroupHeader} | ||
* @param {CanvasRenderingContext2D} gc | ||
* @memberOf groupedHeader | ||
*/ | ||
function drawLinearGradient(gc) { | ||
var bounds = this.bounds; | ||
var grad = gc.createLinearGradient(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height); | ||
this.gradientStops.forEach(function(stop) { | ||
grad.addColorStop.apply(grad, stop); | ||
}); | ||
gc.fillStyle = grad; | ||
gc.fillRect(bounds.x + 2, bounds.y, bounds.width - 3, bounds.height); | ||
} | ||
/** @typedef gradientStop | ||
* @type {Array} | ||
* @desc Consists of two elements: | ||
* 1. Element `[0]` (number): Stop position ranging from `0.0` (start of gradient) to `1.0` (end of gradient). | ||
* 2. Element `[1]` (string): CSS color spec in a string, one of: | ||
* * color name | ||
* * #nnnnnn | ||
* * rgb(r,g,b) | ||
* * rgba(r,g,b,a) | ||
* | ||
* Applied to the graphic context's {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient/addColorStop|addColorStop} method. | ||
*/ | ||
/** | ||
* Group headers are implemented by rendering each header with it's group(s) above it, all in the one header cell. This multi-line header has the highest-order group's label on the top, with each lower-order group's label on successive lines below it, ending with the actual column header. The conceit of this algorithm is that each group level is rendered in successive columns stretched across all the columns thus far rendered. | ||
* | ||
* Column grouping is specified by resetting the all headers involved to a list consisting of the labels of the common group header(s) and ending with the actual column header. For example, to strecth a group label "Age" above columns 4 and 5: | ||
* | ||
* ```javascript | ||
* grid.behavior.getActiveColumn[3].header = "Age|Years"; | ||
* grid.behavior.getActiveColumn[4].header = "Age|Months"; | ||
* ``` | ||
* | ||
* It is usually convenient to use the behavior's `setHeaders` method. This mix-in actually extends that method to make it slightly more useful: In addition to setting the headers as usual, it also increases the header row height to accommodate the deepest group label. (You don't have to use this method; you can simply reset the headers as above and set the row height yourself.) | ||
* | ||
* ### API access | ||
* | ||
* If you're using browserify with the *npm* module: | ||
* ```javascript | ||
* var groupedHeader = require('fin-hypergrid/add-ons/grouped-header'); | ||
* ``` | ||
* | ||
* If you're using the script file on the CDN: | ||
* ```html | ||
* <script src="openfin.github.io/fin-hypergrid/build/fin-hypergrid.min.js"></script> | ||
* <script src="openfin.github.io/fin-hypergrid/build/add-ons/grouped-header.min.js"></script> | ||
* ``` | ||
* ```javascript | ||
* var groupedHeader = fin.hypergrid.groupedHeader; | ||
* ``` | ||
* | ||
* ### Usage | ||
* | ||
* This example specifies that the two named columns are under a group labeled "Name": | ||
* ```javascript | ||
* var grid = new fin.Hypergrid(...); | ||
* groupedHeader.mixInTo(grid, options); | ||
* grid.behavior.setHeaders({ | ||
* firstName: 'Name|First', | ||
* lastName: 'Name|Last' | ||
* }); | ||
* ``` | ||
* | ||
* You can nest headers to any level. The example above is only one level deep. The live {@link http://openfin.github.io/fin-hypergrid/grouped-header.html|demo} demonstrates a nested header (one additional level). | ||
* | ||
* This API uses its own extension of the {@link SimpleCell} cell renderer which does the stretched partial cell renders explained above. It also calls a background paint function to fill the area behind the group labels. This API comes with two such background paint functions, described below. These are both exposed so you can specify one over the other. You can also specify a reference to a custom function. | ||
* | ||
* ### Background paint functions | ||
* | ||
* #### {@link groupedHeader.drawProportionalBottomBorder|drawProportionalBottomBorder} | ||
* This is the default background paint function. It paints a bottom border under the group header whose thickness is in proportion to the order (level) of the group: | ||
* | ||
* ![Bottom Border Background](https://github.com/openfin/fin-hypergrid/raw/master/images/jsdoc/grouped-header/bottom-border.png) | ||
* | ||
* The lowest-order group has a 1-pixel thick border; borders grow progressively thicker with each superior group; the highest-order group has the thickest border. | ||
* | ||
* #### {@link groupedHeader.drawLinearGradient|drawLinearGradient} | ||
* This paints a background that transitions color from top to bottom: | ||
* | ||
* ![Linear Gradient Background](https://github.com/openfin/fin-hypergrid/raw/master/images/jsdoc/grouped-header/gradient.png) | ||
* | ||
* ### Options | ||
* | ||
* Options are supplied to the {@link groupedHeader.mixInTo|mixInTo} call and apply to the entire grid so that all column groups in a grid share the same appearance. Most options (with the exception of `delimiter`) are for the use of the {@link groupedHeader.mixInTo~GroupedHeader|GroupedHeader} cell renderer. | ||
* | ||
* #### `paintBackground` option | ||
* | ||
* To override the default background paint function, pass a reference in the `backgroundPaint` option: | ||
* ```javascript | ||
* fin.Hypergrid.groupedHeader.mixInTo(grid, { | ||
* paintBackground: groupedHeader.drawLinearGradient | ||
* }); | ||
* ``` | ||
* | ||
* #### `gradientStops` option | ||
* | ||
* Gradients blend between a series of colors filling the area behind the group label from top to bottom. Each color may include an opacity level. | ||
* | ||
* The gradient illustrated above is the default. It uses the default header label color to fill the background, transitioning from top alpha=0% to bottom alpha=35%. | ||
* | ||
* You can however specify your own {@link https://developer.mozilla.org/en-US/docs/Web/API/CanvasGradient/addColorStop|gradient stops}: | ||
* | ||
* ```javascript | ||
* fin.Hypergrid.groupedHeader.mixInTo(grid, { | ||
* paintBackground: fin.Hypergrid.groupedHeader.drawLinearGradient, | ||
* gradientStops: [ | ||
* [0, 'rgba(255, 255, 0, 0)'], | ||
* [1, 'rgba(255, 255, 0, .5)'] | ||
* ], | ||
* groupColor: 'red' | ||
*}); | ||
* ``` | ||
* | ||
* The gradient shown above fills the background, transitioning from top (0) in yellow (`255, 255, 0`) with alpha=0% (`0`) to bottom (`1`) with alpha=50% (`.5`): | ||
* | ||
* ![Linear Gradient Background (Yellow)](https://github.com/openfin/fin-hypergrid/raw/master/images/jsdoc/grouped-header/gradient-yellow.png) | ||
* | ||
* #### `groupColor` option | ||
* | ||
* The example above also illustrates setting the `groupColor` option which specifies the font color for the group labels, in this case red (yuck! but it's just an example). | ||
* | ||
* #### `delimiter` option | ||
* | ||
* Allows you to change the delimiter in the header strings from the default vertical bar character (`'|'`) to something else. | ||
* | ||
* @mixin | ||
*/ | ||
var groupedHeader = { | ||
mixInTo: mixInTo, | ||
setHeaders: setHeaders, | ||
drawProportionalBottomBorder: drawProportionalBottomBorder, | ||
drawLinearGradient: drawLinearGradient | ||
}; | ||
module.exports = groupedHeader; |
'use strict'; | ||
// NOTE: gulpfile.js's 'add-ons' task makes a copy of this file, altering the final line. The copy is placed in demo/build/add-ons/ along with a minified version. Both files are eventually deployed to http://openfin.github.io/fin-hypergrid/add-ons/. Neither file is saved to the repo. | ||
/** | ||
@@ -7,7 +9,6 @@ * @classdesc This is a simple helper class to set up the tree-view data source in the context of a hypergrid. | ||
* It includes methods to: | ||
* * Insert `DataSourceTreeview` into the data model's pipeline (`addPipe`, `addPipeTo`). | ||
* * Perform the self-join and rebuild the index to turn the tree-view on or off, optionally hiding the ID columns (`setRelation`). | ||
* * Insert the tree-view data source (`DataSourceTreeview`) into the data model's pipeline (see {#@link TreeView#setPipeline|setPipeline} method) along with the optional filter and sort data sources. | ||
* * Perform the self-join and rebuild the index to turn the tree-view on or off, optionally hiding the ID columns ({#@link TreeView#setRelation|setRelation} method). | ||
* | ||
* @param {object} [options] | ||
* @param {boolean} [options.shared=false] | ||
* @param {object} options - Passed to data source's {@link DataSourceTreeView#setRelation|setRelation} method ({@link http://openfin.github.io/hyper-analytics/DataSourceTreeview.html#setRelation|see}) when called here by local API's {@link TreeView#setRelation|this.setRelation} method. | ||
* @constructor | ||
@@ -17,23 +18,57 @@ */ | ||
this.grid = grid; | ||
this.options = options; | ||
this.options = options || {}; | ||
} | ||
TreeView.prototype = { | ||
constructor: TreeView.prototype.constructor, | ||
/** | ||
* @summary Reconfigure the dataModel's pipeline for tree view. | ||
* @desc The pipeline is reset starting with either the given `options.dataSource` _or_ the existing pipeline's first data source. | ||
* @summary Reconfigure the data model's data pipeline for tree view. | ||
* @desc The _data transformation pipeline_ is an ordered list of data transformations, always beginning with an actual data source. Each _transformation_ in the pipeline operates on the data source immediately ahead of it. While transformations are free to completely rewrite the data in any way they want, most transformations merely apply an index to the data. | ||
* | ||
* Then the tree view filter and sorter data sources are added as requested. | ||
* The _shared pipeline_ is defined on the data model's prototype and is used for all grid instances (using the same data model). A grid can however define a _local pipeline_ on the data model's instance. | ||
* | ||
* Finally the tree view data source is added. | ||
* In any case, the actual data pipeline is (re)constructed from a _pipeline configuration_ each time data is set on the grid via {@link dataModel/JSON#setData|setData}. | ||
* | ||
* This method can operate on either: | ||
* * A data model prototype, which will affect all data models subsequently created therefrom. The prototype must be given in `options.dataModelPrototype`. | ||
* * The current data model instance. In this case, the instance is given its own new pipeline. | ||
* This method reconfigures the data pipeline suitable for tree view. It is designed to operate on either of: | ||
* * the "shared" pipeline configuration (on the grid's data model's prototype) | ||
* * the grid's "local" pipeline configuration (on the grid's data model's instance) | ||
* | ||
* This method operates as follows: | ||
* 1. Reset the pipeline: | ||
* * In the case of the shared pipeline, the array is truncated in place. | ||
* * In the case of an instance pipeline, a new array is created. | ||
* 2. Add the first data source: | ||
* * The data source provided in `options.firstPipe` is used if given; otherwise... | ||
* * The existing pipeline configuration's first data source will be reused. (First time in, this will always come from the prototype's version.) | ||
* 3. Add the filter data source (if requested). | ||
* 4. Add the tree sorter data source (if requested). | ||
* 5. Finally, add the tree view data source. | ||
* | ||
* Step 1 above operates on the shared pipeline when you supply the data model's prototype in `options.dataModelPrototype` (see below). In this case, you have the option of calling this method _before_ instantiating your grid(s): | ||
* | ||
* ```javascript | ||
* var JSON = Hypergrid.dataModels.JSON.prototype; | ||
* var pipelineOptions = { dataModelPrototype: JSON.prototype } | ||
* TreeView.prototype.setPipeline(pipelineOptions); | ||
* ``` | ||
* | ||
* This approach avoids the need to reset the data after reconfiguring the pipeline (in which case, do _not_ call this method again after instantiation). | ||
* | ||
* @param {object} [options] | ||
* @param {object} [options.dataModelPrototype] - Adds the pipes to the given object. If omitted, this must be an instance; adds the pipes to a new "own" pipeline created from the first data source of the instance's old pipeline. | ||
* @param {dataSourcePipelineObject} [options.firstPipe] - Use as first data source in the new pipeline. If omitted, re-uses the existing pipeline's first data source. | ||
* | ||
* @param {boolean} [options.includeFilter=false] - Enables filtering. Includes the filter data source. The filter row is hidden if falsy. | ||
* | ||
* @param {boolean} [options.includeSorter=false] - Enables sorting. Includes the specialized tree sorter data source. | ||
* | ||
* @param {object} [options.dataModelPrototype] - Adds requested pipes to the "shared" pipeline array object instead of to a new custom (instance) pipeline array object. | ||
* | ||
* Supply this option when you want to set up the "shared" pipeline on the data model prototype, which would then be available to all grid instances subsequently created thereafter. In this case, you can call this method before or after grid instantiation. To call it before, call it directly on `TreeView.prototype`; to call it after, call it normally (on the `TreeView` instance). | ||
* | ||
* If omitted, a new "own" (instance) pipeline is created, overriding the prototype's (shared) pipeline. (In this case this method must be called normally, on the `Treeview` instance.) | ||
* | ||
* In either case, if called "normally" (on the instance), the data is reset via `setData`. (If called on the prototype it is not reset here. Currently the `Hypergrid` constructor calls it.) | ||
* | ||
* @param {dataSourcePipelineObject} [options.firstPipe] - Use as first data source in the new pipeline. If undefined, the existing pipeline's first data source will be reused. | ||
*/ | ||
@@ -83,3 +118,3 @@ setPipeline: function(options) { | ||
* @summary Build/unbuild the tree view. | ||
* @param {boolean} join - Turn tree-view **ON**. If falsy (or omitted), turn it **OFF**. | ||
* @param {boolean} join - If truthy, turn tree-view **ON**. If falsy (or omitted), turn it **OFF**. | ||
* @param {boolean} [hideIdColumns=false] - Once hidden, cannot be unhidden from here. | ||
@@ -98,7 +133,7 @@ * @returns {boolean} Joined state. | ||
if (joined) { | ||
// save the current value of column's editable property and set it to false | ||
// Make the tree column uneditable: Save the current value of the tree column's editable property and set it to false. | ||
this.editableWas = !!columnProps.editable; | ||
columnProps.editable = false; | ||
// save value of grid's checkboxOnlyRowSelections property and set it to true so drill-down clicks don't select the row they are in | ||
// Save value of grid's checkboxOnlyRowSelections property and set it to true so drill-down clicks don't select the row they are in | ||
this.checkboxOnlyRowSelectionsWas = state.checkboxOnlyRowSelections; | ||
@@ -117,12 +152,3 @@ state.checkboxOnlyRowSelections = true; | ||
} | ||
dataSource.defaultSortColumn = dataSource.getColumnInfo(options.defaultSortColumn, dataSource.treeColumn.name); | ||
// If unsorted, sort by tree column | ||
if (behavior.getSortedColumnIndexes().length === 0) { | ||
var gridIndex = behavior.getActiveColumnIndex(dataSource.defaultSortColumn.index); | ||
this.grid.toggleSort(gridIndex, []); | ||
} | ||
} else { | ||
dataSource.defaultSortColumn = undefined; | ||
columnProps.editable = this.editableWas; | ||
@@ -140,2 +166,3 @@ state.checkboxOnlyRowSelections = this.checkboxOnlyRowSelectionsWas; | ||
} | ||
}; | ||
@@ -147,2 +174,3 @@ | ||
* @returns {boolean} If the data source is a tree view. | ||
* @private | ||
*/ | ||
@@ -149,0 +177,0 @@ function isTreeview(event) { |
@@ -1,2 +0,2 @@ | ||
module.exports = { // This file generated by gulp-imagine-64 at 11:33:57 PM on 8/7/2016 | ||
module.exports = { // This file generated by gulp-imagine-64 at 9:23:15 PM on 8/28/2016 | ||
"calendar": { | ||
@@ -3,0 +3,0 @@ type: "image/png", |
{ | ||
"name": "fin-hypergrid", | ||
"version": "1.0.8", | ||
"version": "1.0.9", | ||
"description": "Canvas-based high-performance spreadsheet", | ||
@@ -20,7 +20,8 @@ "repository": { | ||
"automat": "1.2.0", | ||
"chai": "^3.5.0", | ||
"extend-me": "2.2.4", | ||
"filter-tree": "0.3.34", | ||
"filter-tree": "0.3.38", | ||
"finbars": "1.5.1", | ||
"fincanvas": "1.3.0", | ||
"hyper-analytics": "0.11.7", | ||
"hyper-analytics": "0.11.13", | ||
"fincanvas": "1.4.0", | ||
"list-dragon": "1.3.3", | ||
@@ -27,0 +28,0 @@ "lru-cache": "2.7.0", |
@@ -0,1 +1,3 @@ | ||
[![Build Status](https://travis-ci.org/openfin/fin-hypergrid.svg?branch=develop)](https://travis-ci.org/openfin/fin-hypergrid) | ||
**fin-hypergrid** is an ultra-fast HTML5 grid presentation layer, achieving its speed by rendering (in a canvas tag) only the currently visible portion of your (virtual) grid, thus avoiding the latency and life-cycle issues of building, walking, and maintaining a complex DOM structure. | ||
@@ -5,5 +7,5 @@ | ||
### Current Release (1.0.7 - 18 July 2016) | ||
### Current Release (1.0.9 - 29 August 2016) | ||
The current version replaces last year's [prototype version](https://github.com/openfin/fin-hypergrid/tree/polymer-prototype), which was built around Polymer. It is now completely "de-polymerized" and is being made available as: | ||
The current version 1.0 replaces last year's [prototype version](https://github.com/openfin/fin-hypergrid/tree/polymer-prototype), which was built around Polymer. It is now completely "de-polymerized" and is being made available as: | ||
* An [npm module](https://www.npmjs.com/package/fin-hypergrid) for use with browserify. | ||
@@ -26,46 +28,19 @@ * A single JavaScript file [fin-hypergrid.js](https://openfin.github.io/fin-hypergrid/build/fin-hypergrid.js) you can reference in a `<script>` tag. | ||
![](https://github.com/openfin/fin-hypergrid/blob/master/images/Hyperblotter%20Tabled%20Reduced%20Rows.png) | ||
![](https://github.com/openfin/fin-hypergrid/blob/master/images/README/Hyperblotter%20Tabled%20Reduced%20Rows.png) | ||
### Features | ||
| Ultra High Performance || General | ||
|----------------------------|----|---------------------------- | ||
| Any number of rows and columns <br> (100k+)| | Cross-browser support | ||
| Infinite, smooth scrolling | | Clipboard copy & paste | ||
| Scrolling via cell | | Localization | ||
| Local (client-side) and remote <br> (server-side) data hosting | | Context menu | ||
![](https://github.com/openfin/fin-hypergrid/blob/master/images/README/Hypergrid%20Features.png) | ||
| Flexible Data Views & Selection || Look-and-Feel | ||
|----------------------------|----|---------------------------- | ||
| Filtering and sorting | | CSS Themes | ||
| Aggregation | | Grid, column, row, and <br> cell styling | ||
| Tree view (drill-downs) and group <br> view | | Custom cell editors | ||
| Inline editing | | Customizable scroll bars | ||
| Column grouping, including <br> multi-level | | Column and row resizing | ||
| Freeze columns and rows | | Column auto-sizing | ||
| Cell range, checkbox, and row <br> selection | | Column dragging | ||
| Keyboard navigation | | | ||
| Available in the OpenFin Container | ||
|-------------------------------------------------------- | ||
| CSV export | ||
| Cross app communication | ||
| Cross app docking | ||
| Printing | ||
| Easy deployment | ||
| Cross browser independence | ||
##### Future development | ||
* Tree-view presentation for remotely aggregated data | ||
* We are currently working on expanding the API to enable application developers to easily provide their own functionality. | ||
* Hypergrid will have no opinion on how the underlying data should be pivoted, but will remain capable of presenting pivoted data. | ||
* For local data transformations, the current sorting, filtering, _etc.,_ modules will be unbundled from the Hypergrid build and published as external modules, along with full API docs so you can roll your own. | ||
* Remote data transformations will be supported with all the eventing necessary for triggering such transformations on a remote server. | ||
* Drill-downs, currently implemented for local tree view, group view, and aggregated data view, will be supported for remote data as well. | ||
###### The Filtering & Analytics (sorting & aggregation) modules provided will be broken out of Hypergrid | ||
* We are currently working on expanding the API to enable application developers to easily provide their own functionality | ||
* Hypergrid will have no opinion on how the underlying data should be pivoted, but will remain capable of presenting pivoted data | ||
* The current filtering and analytics modules will become separate npm modules/JavaScript files that can be forked and further developed | ||
### Integrating | ||
[This](https://openfin.github.io/fin-hypergrid/example.js) is a basic example that embeds fin-hypergrid: | ||
[This](https://openfin.github.io/fin-hypergrid/example.html) is a basic example that embeds fin-hypergrid: | ||
```html | ||
@@ -121,1 +96,2 @@ <!DOCTYPE html> | ||
Developers interested in contributing to this project should submit pull requests against the `develop` branch. | ||
We have several beginner `help wanted` [tickets](https://github.com/openfin/fin-hypergrid/issues) open for community involvement. |
@@ -80,6 +80,2 @@ 'use strict'; | ||
applyAnalytics: function() { | ||
this.dataModel.applyAnalytics(); | ||
}, | ||
/** | ||
@@ -90,3 +86,3 @@ * @memberOf behaviors.JSON.prototype | ||
* * _If an array:_ Must contain all headers in column order. | ||
* * _If a hash:_ May contain any headers, keyed by field name, in any order (of course). | ||
* * _If a hash:_ May contain any headers, keyed by field name, in any order. | ||
*/ | ||
@@ -235,7 +231,12 @@ setHeaders: function(headers) { | ||
getColumnAlignment: function(x) { | ||
if (x === 0 && this.hasHierarchyColumn()) { | ||
return 'left'; | ||
var align; | ||
if (x === -1) { | ||
align = 'right'; | ||
} else if (x === 0 && this.hasHierarchyColumn()) { | ||
align = 'left'; | ||
} else { | ||
return 'center'; | ||
align = this.getColumnProperties(x).halign; | ||
} | ||
return align; | ||
}, | ||
@@ -242,0 +243,0 @@ getHiddenColumns: function() { |
@@ -244,3 +244,3 @@ /* eslint-env browser */ | ||
} else { // invalid but no feedback | ||
return this.cancelEditing(); | ||
this.cancelEditing(); | ||
} | ||
@@ -247,0 +247,0 @@ |
@@ -66,2 +66,3 @@ 'use strict'; | ||
} | ||
if (gc.textAlign !== 'left') { | ||
@@ -367,3 +368,3 @@ gc.textAlign = 'left'; | ||
var result = vf; | ||
if (config.isGridColumn && config.isGridRow) { | ||
if (config.isGridColumn && config.isGridRow && config.dataRow) { | ||
calculator = (typeof vf)[0] === 'f' && vf || calculator; | ||
@@ -370,0 +371,0 @@ if (calculator) { |
@@ -97,3 +97,3 @@ 'use strict'; | ||
getData: function() { | ||
return this.sources.source.data; | ||
return this.source.data; | ||
}, | ||
@@ -225,3 +225,3 @@ | ||
} else if (isFilterRow) { | ||
this.setFilter(x, value, { alert: true }); | ||
this.setFilter(x, value); | ||
} else { | ||
@@ -304,7 +304,7 @@ return this._setHeader(x, value); | ||
getCalculators: function() { | ||
return this.dataSource.getCalculators(); | ||
return this.dataSource.getProperty('calculators'); | ||
}, | ||
/** @typedef {object} dataSourcePipelineObject | ||
* @property {function} DataSource - A `hyper-analytics`-style "data source" constructor. | ||
* @property {string} type - A `hyper-analytics`-style "data source" constructor name. | ||
* @property {*} [options] - When defined, passed as 2nd argument to constructor. | ||
@@ -521,3 +521,3 @@ * @property {string} [parent] - Defines a branch off the main sequence. | ||
* @memberOf dataModels.JSON.prototype | ||
* @param {number} colIndex | ||
* @param {number} columnIndex | ||
* @param {boolean} deferred | ||
@@ -527,3 +527,3 @@ */ | ||
var sorts = this.getSortedColumnIndexes(), | ||
sortPosition, found; | ||
sortPosition; | ||
@@ -534,21 +534,3 @@ if (sorts.find(function(sortSpec, index) { | ||
})) { | ||
if (sorts.length === 1) { | ||
for (var dataSource = this.dataSource; dataSource; dataSource = dataSource.dataSource) { | ||
if (dataSource.defaultSortColumn) { | ||
found = true; | ||
break; | ||
} | ||
} | ||
} | ||
if (found) { | ||
// Make the sole remaining sorted column the tree column of the "joined" data source | ||
sorts[0] = { | ||
columnIndex: dataSource.defaultSortColumn.index, | ||
direction: 1 | ||
}; | ||
} else { | ||
sorts.splice(sortPosition, 1); | ||
} | ||
sorts.splice(sortPosition, 1); | ||
if (!deferred) { | ||
@@ -841,8 +823,24 @@ this.applyAnalytics({columnSort: true}); | ||
getUnfilteredValue: function(x, y) { | ||
return this.sources.source.getValue(x, y); | ||
return this.source.getValue(x, y); | ||
}, | ||
getUnfilteredRowCount: function() { | ||
return this.sources.source.getRowCount(); | ||
} | ||
return this.source.getRowCount(); | ||
}, | ||
/** | ||
* @summary Add a new data row to the grid. | ||
* @desc If data source pipeline in use, to see the new row in the grid, you must eventually call: | ||
* ```javascript | ||
* this.grid.behavior.applyAnalytics(); | ||
* this.grid.behaviorChanged(); | ||
* ``` | ||
* @param {object} newDataRow | ||
* @returns {object} The new row object. | ||
* @memberOf dataModels.JSON.prototype | ||
*/ | ||
addRow: function(newDataRow) { | ||
this.getData().push(newDataRow); | ||
return newDataRow; | ||
}, | ||
}); | ||
@@ -849,0 +847,0 @@ |
@@ -19,3 +19,3 @@ /* eslint-env browser */ | ||
* @default | ||
* @type {cssFont} | ||
* @type {string} | ||
* @instance | ||
@@ -368,2 +368,8 @@ */ | ||
fixedColAlign: 'center', | ||
/** | ||
* @default | ||
* @type {string} | ||
* @instance | ||
*/ | ||
halign: 'center', | ||
@@ -548,3 +554,3 @@ /** | ||
/** Clicking in a row header (leftmost column) "selects" the row; the entire row is added to the select region and repainted with "row selection" colors. | ||
/** Clicking in a column header (top row) "selects" the column; the entire column is added to the select region and repainted with "column selection" colors. | ||
* @default | ||
@@ -556,3 +562,3 @@ * @type {boolean} | ||
/** Clicking in a column header (top row) "selects" the column; the entire column is added to the select region and repainted with "column selection" colors. | ||
/** Clicking in a row header (leftmost column) "selects" the row; the entire row is added to the select region and repainted with "row selection" colors. | ||
* @default | ||
@@ -736,2 +742,7 @@ * @type {boolean} | ||
/** Allow multiple cell region selections. | ||
* @type {boolean} | ||
* @default | ||
*/ | ||
multipleSelections: false, | ||
}; | ||
@@ -738,0 +749,0 @@ |
@@ -25,4 +25,7 @@ /* eslint-env browser */ | ||
if (behavior.isColumnReorderable()) { | ||
// grab the lists from the behavior | ||
if (behavior.setGroups){ | ||
// parse & add the drag-and-drop stylesheet addendum | ||
var stylesheetAddendum = stylesheet.inject('list-dragon-addendum'); | ||
// grab the group lists from the behavior | ||
if (behavior.setGroups) { | ||
this.selectedGroups = { | ||
@@ -37,4 +40,14 @@ title: 'Groups', | ||
}; | ||
var groupPicker = new ListDragon([ | ||
this.selectedGroups, | ||
this.availableGroups | ||
]); | ||
// add the drag-and-drop sets to the dialog | ||
this.append(groupPicker.modelLists[0].container); | ||
this.append(groupPicker.modelLists[1].container); | ||
} | ||
// grab the column lists from the behavior | ||
this.inactiveColumns = { | ||
@@ -52,32 +65,18 @@ title: 'Inactive Columns', | ||
// parse & add the drag-and-drop stylesheet addendum | ||
var stylesheetAddendum = stylesheet.inject('list-dragon-addendum'); | ||
var columnPicker = new ListDragon([ | ||
this.inactiveColumns, | ||
this.activeColumns | ||
], { | ||
// add the list-dragon-base stylesheet right before the addendum | ||
cssStylesheetReferenceElement: stylesheetAddendum, | ||
// these models have a header property as their labels | ||
label: '{header}' | ||
}); | ||
// create drag-and-drop sets from the lists | ||
var listSets = [ | ||
new ListDragon([ | ||
this.selectedGroups, | ||
this.availableGroups | ||
], { | ||
// add the list-dragon-base stylesheet right before the addendum | ||
cssStylesheetReferenceElement: stylesheetAddendum | ||
}), | ||
new ListDragon([ | ||
this.inactiveColumns, | ||
this.activeColumns | ||
], { | ||
// these models have a header property as their labels | ||
label: '{header}' | ||
}) | ||
]; | ||
// add the drag-and-drop sets to the dialog | ||
this.append(columnPicker.modelLists[0].container); | ||
this.append(columnPicker.modelLists[1].container); | ||
// add the drag-and-drop sets to the dialog | ||
var self = this; | ||
listSets.forEach(function(listSet) { | ||
listSet.modelLists.forEach(function(list) { | ||
self.append(list.container); | ||
}); | ||
}); | ||
//Listen to the visible column changes | ||
listSets[1].modelLists[1].element.addEventListener('listchanged', function(e){ | ||
columnPicker.modelLists[1].element.addEventListener('listchanged', function(e){ | ||
grid.fireSyntheticOnColumnsChangedEvent(); | ||
@@ -84,0 +83,0 @@ }); |
@@ -71,3 +71,3 @@ 'use strict'; | ||
if (isVisibleChar) { | ||
editor.setEditorValue(char); | ||
editor.input.value = char; | ||
} else if (isDeleteChar) { | ||
@@ -74,0 +74,0 @@ editor.setEditorValue(''); |
@@ -75,7 +75,3 @@ 'use strict'; | ||
if (!grid.isCellSelection() || isRightClick || isHeader || isOutside) { | ||
if (this.next) { | ||
this.next.handleMouseDown(grid, event); | ||
} | ||
} else { | ||
if (grid.isCellSelection() && !(isRightClick || isHeader || isOutside)) { | ||
var numFixedColumns = grid.getFixedColumnCount(); | ||
@@ -100,2 +96,4 @@ var numFixedRows = grid.getFixedRowCount(); | ||
this.extendSelection(grid, dCell, keys); | ||
} else if (this.next) { | ||
this.next.handleMouseDown(grid, event); | ||
} | ||
@@ -113,8 +111,3 @@ }, | ||
if (!grid.isCellSelection() || isRightClick || !this.dragging) { | ||
if (this.next) { | ||
this.next.handleMouseDrag(grid, event); | ||
} | ||
} else { | ||
if (this.dragging && grid.isCellSelection() && !isRightClick) { | ||
var numFixedColumns = grid.getFixedColumnCount(); | ||
@@ -146,2 +139,4 @@ var numFixedRows = grid.getFixedRowCount(); | ||
this.handleMouseDragCellSelection(grid, dCell, primEvent.detail.keys); | ||
} else if (this.next) { | ||
this.next.handleMouseDrag(grid, event); | ||
} | ||
@@ -322,4 +317,4 @@ }, | ||
grid.clearMostRecentSelection(); | ||
grid.select(mousePoint.x, mousePoint.y, x - mousePoint.x + 1, y - mousePoint.y + 1); | ||
grid.setDragExtent(grid.newPoint(x - mousePoint.x + 1, y - mousePoint.y)); | ||
grid.select(mousePoint.x, mousePoint.y, x - mousePoint.x, y - mousePoint.y); | ||
grid.setDragExtent(grid.newPoint(x - mousePoint.x, y - mousePoint.y)); | ||
} else { | ||
@@ -326,0 +321,0 @@ grid.select(x, y, 0, 0); |
@@ -338,3 +338,3 @@ 'use strict'; | ||
} | ||
options.throw = true; | ||
error = subexpression.invalid(options); | ||
@@ -341,0 +341,0 @@ } |
@@ -47,3 +47,8 @@ 'use strict'; | ||
// Escape all symbolic (non alpha) operators. | ||
operators = operators.map(function(op) { return /[^\w]/.test(op) ? '\\' + op.split('').join('\\') : op; }); | ||
operators = operators.map(function(op) { | ||
if (/^[^A-Z]/.test(op)) { | ||
op = '\\' + op.split('').join('\\'); | ||
} | ||
return op; | ||
}); | ||
@@ -50,0 +55,0 @@ var symbolicOperators = operators.filter(function(op) { return op[0] === '\\'; }), |
@@ -40,2 +40,35 @@ /* eslint-env browser */ | ||
// Safari has no Intl implementation | ||
window.Intl = window.Intl || { | ||
NumberFormat: function(locale, options) { | ||
var digits = '0123456789'; | ||
this.format = function(n) { | ||
var s = n.toString(); | ||
if (!options || options.useGrouping === undefined || options.useGrouping) { | ||
var dp = s.indexOf('.'); | ||
if (dp < 0) { dp = s.length; } | ||
while ((dp -= 3) > 0 && digits.indexOf(s[dp - 1]) >= 0) { | ||
s = s.substr(0, dp) + ',' + s.substr(dp); | ||
} | ||
} | ||
return s; | ||
}; | ||
}, | ||
DateTimeFormat: function(locale, options) { | ||
this.format = function(date) { | ||
if (date != null) { | ||
if (typeof date !== 'object') { | ||
date = new Date(date); | ||
} | ||
date = date.getMonth() + 1 + '-' + date.getDate() + '-' + date.getFullYear(); | ||
} else { | ||
date = null; | ||
} | ||
return date; | ||
}; | ||
} | ||
}; | ||
/** | ||
@@ -76,3 +109,3 @@ * @summary Create a number localizer. | ||
* | ||
* For internal use by the {@link NumberFormatter#standardize|standardize} method. | ||
* For internal use by the {@link NumberFormatter#parse|parse} method. | ||
* @memberOf NumberFormatter.prototype | ||
@@ -121,3 +154,3 @@ */ | ||
* 1. Filter out invalid characters on a `onkeydown` event; or | ||
* 2. Test an edited string prior to calling the {@link module:localization~NumberFormatter#standardize|standardize}. | ||
* 2. Test an edited string prior to calling the {@link module:localization~NumberFormatter#parse|parse}. | ||
* | ||
@@ -238,3 +271,3 @@ * NOTE: This method does not check grammatical syntax; it only checks for invalid characters. | ||
'[^' + | ||
localizedDate + | ||
localizedDate.replace(/-/g, '\\-') + | ||
missingDigits + | ||
@@ -254,3 +287,3 @@ ']' | ||
* 1. Filter out invalid characters on a `onkeydown` event; or | ||
* 2. Test an edited string prior to calling the {@link module:localization~DateFormatter#standardize|standardize}. | ||
* 2. Test an edited string prior to calling the {@link module:localization~DateFormatter#parse|parse}. | ||
* | ||
@@ -257,0 +290,0 @@ * NOTE: The current implementation only supports date formats using all numerics (which is the default for `Intl.DateFormat`). |
@@ -463,3 +463,3 @@ /* eslint-env browser */ | ||
var colWall = this.getColumnEdges()[this.getColumnEdges().length - chop]; | ||
var result = Math.min(colWall, this.getBounds().width - 200); | ||
var result = Math.min(colWall, this.getBounds().width); | ||
return result; | ||
@@ -1023,3 +1023,2 @@ }, | ||
} | ||
cellProperties.halign = 'right'; | ||
} else { | ||
@@ -1033,5 +1032,5 @@ // set dataRow and columnName used by valOrFunc (needed when func) | ||
cellProperties.value = grid.getValue(c, r); | ||
cellProperties.halign = grid.getColumnAlignment(c); | ||
} | ||
cellProperties.halign = grid.getColumnAlignment(c); | ||
cellProperties.isGridColumn = isGridColumn; | ||
@@ -1038,0 +1037,0 @@ cellProperties.isGridRow = isGridRow; |
@@ -16,2 +16,10 @@ 'use strict'; | ||
/** | ||
* @name multipleSelections | ||
* @type {boolean} | ||
* @summary Can select multiple cell regions. | ||
* @memberOf SelectionModel.prototype | ||
*/ | ||
this.multipleSelections = grid[grid.behavior ? 'getProperties' : '_getProperties']().multipleSelections; | ||
/** | ||
* @name selections | ||
@@ -112,15 +120,27 @@ * @type {Rectangle[]} | ||
var newSelection = this.grid.newRectangle(ox, oy, ex, ey); | ||
newSelection.firstSelectedCell = this.grid.newPoint(ox, oy); //Cache the first selected cell before it gets normalized to top-left origin | ||
//Cache the first selected cell before it gets normalized to top-left origin | ||
newSelection.firstSelectedCell = this.grid.newPoint(ox, oy); | ||
newSelection.lastSelectedCell = ( | ||
(newSelection.firstSelectedCell.x === newSelection.origin.x && newSelection.firstSelectedCell.y === newSelection.origin.y) | ||
? | ||
newSelection.corner | ||
: | ||
newSelection.origin | ||
); | ||
this.selections.push(newSelection); | ||
this.flattenedX.push(newSelection.flattenXAt(0)); | ||
this.flattenedY.push(newSelection.flattenYAt(0)); | ||
newSelection.firstSelectedCell.x === newSelection.origin.x && | ||
newSelection.firstSelectedCell.y === newSelection.origin.y | ||
) | ||
? newSelection.corner | ||
: newSelection.origin; | ||
if (this.multipleSelections) { | ||
this.selections.push(newSelection); | ||
this.flattenedX.push(newSelection.flattenXAt(0)); | ||
this.flattenedY.push(newSelection.flattenYAt(0)); | ||
} else { | ||
this.selections[0] = newSelection; | ||
this.flattenedX[0] = newSelection.flattenXAt(0); | ||
this.flattenedY[0] = newSelection.flattenYAt(0); | ||
} | ||
this.setLastSelectionType('cell'); | ||
if (!silent) {this.grid.selectionChanged();} | ||
if (!silent) { | ||
this.grid.selectionChanged(); | ||
} | ||
}, | ||
@@ -397,2 +417,8 @@ | ||
deselectRow: function(y1, y2) { | ||
if (this.areAllRowsSelected()) { | ||
// To deselect a row, we must first remove the all rows flag... | ||
this.setAllRowsSelected(false); | ||
// ...and create a single range representing all rows | ||
this.rowSelectionModel.select(this.grid.getHeaderRowCount(), this.grid.getRowCount() - 1); | ||
} | ||
this.rowSelectionModel.deselect(y1, y2); | ||
@@ -399,0 +425,0 @@ this.setLastSelectionType('row'); |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
769736
17292
15
96
95
+ Addedchai@^3.5.0
+ Addedassertion-error@1.1.0(transitive)
+ Addedchai@3.5.0(transitive)
+ Addeddeep-eql@0.1.3(transitive)
+ Addedfilter-tree@0.3.38(transitive)
+ Addedfincanvas@1.4.0(transitive)
+ Addedhyper-analytics@0.11.13(transitive)
+ Addedtype-detect@0.1.11.0.0(transitive)
- Removedfilter-tree@0.3.34(transitive)
- Removedfincanvas@1.3.0(transitive)
- Removedhyper-analytics@0.11.7(transitive)
Updatedfilter-tree@0.3.38
Updatedfincanvas@1.4.0
Updatedhyper-analytics@0.11.13