Skip to content

Commit

Permalink
Merge pull request #762 from joneit/v3.0.4
Browse files Browse the repository at this point in the history
Hypergrid v3.1.0
  • Loading branch information
joneit authored Oct 30, 2018
2 parents cee6141 + 15acc81 commit bfb75d5
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 78 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
**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. Please be sure to checkout our [design overview](OVERVIEW.md)

Below is an example custom application built on top of the Hypergrid API tooling.
It also highlights a DOM-based custom external editor triggered via hypergrid events as well as interaction with Hypergrid's column ordering API
It also highlights a DOM-based custom external editor triggered via hypergrid events as well as interaction with Hypergrids column ordering API.

<img src="images/README/gridshot04.gif">

Expand All @@ -15,17 +15,17 @@ It also highlights a DOM-based custom external editor triggered via hypergrid ev
* [Roadmap](#roadmap)
* [Contributing](#contributors)

### Current Release (3.0.3 - 25 September 2018)
### Current Release (3.1.0 - 29 September 2018)

**Hypergrid 3.0 includes a revised data model with some breaking changes.**
**Hypergrid 3.1 includes 3.0’s revised data model with some breaking changes.**

_For a complete list of changes, see the [release notes](https://github.com/fin-hypergrid/core/releases)._

### Distribution

#### npm module _(recommended)_
Published as a CommonJS module to npmjs.org.
Specify a <a href="https://semver.org/">SEMVER</a> of `"fin-hypergrid": "3.0.3"` (or `"^3.0.3"`) in your package.json file,
Specify a <a href="https://semver.org/">SEMVER</a> of `"fin-hypergrid": "3.1.0"` (or `"^3.1.0"`) in your package.json file,
issue the `npm install` command, and let your bundler (<a target="webpack" href="https://webpack.js.org/">wepback</a>,
<a target="browserify" href="http://browserify.org/">Browserify</a>) create a single file containing both Hypergrid and your application.

Expand Down Expand Up @@ -69,7 +69,7 @@ The [Perspective](https://github.com/jpmorganchase/perspective) open source proj

##### AdaptableBlotter.JS

[Openfin](http://openfin.co)'s AdaptableBlotter.JS ([installer](https://install.openfin.co/download/?fileName=adaptable_blotter_openfin&config=http://beta.adaptableblotter.com/app-beta.json)) is a demo app that shows the capabilities of both Openfin and Hypergrid.
[Openfin](http://openfin.co)s AdaptableBlotter.JS ([installer](https://install.openfin.co/download/?fileName=adaptable_blotter_openfin&config=http://beta.adaptableblotter.com/app-beta.json)) is a demo app that shows the capabilities of both Openfin and Hypergrid.

![](images/README/partner-adaptableblotter_image-01@[email protected])

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fin-hypergrid",
"version": "3.0.3",
"version": "3.1.0",
"description": "Canvas-based high-performance grid",
"main": "src/Hypergrid",
"repository": {
Expand All @@ -25,7 +25,7 @@
"datasaur-base": "^3.0.0",
"datasaur-local": "^3.0.0",
"extend-me": "^2.7.0",
"finbars": "^1.6.0",
"finbars": "^2.0.0",
"inject-stylesheet-template": "^1.0.1",
"mustache": "^2.3.0",
"object-iterators": "1.3.0",
Expand Down
47 changes: 20 additions & 27 deletions src/Hypergrid/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,6 @@ var EDGE_STYLES = ['top', 'bottom', 'left', 'right'],
* @constructor
* @classdesc An object representing a Hypergrid.
* @desc The first parameter, `container`, is optional. If omitted, the `options` parameter is promoted to first position. (Note that the container can also be given in `options.container.`)
* #### `options.canvasContextAttributes` object (see below)
* The only currently meaningful property of this object is `alpha`:
*
* ```js
* var gridOptions = {
* canvasContextAttributes: { alpha: false }
* };
* var myGrid = new Hypergrid(gridOptions);
* ```
*
* `alpha` is a boolean that indicates if the canvas contains an alpha channel. If set to `false`, the browser now knows that the backdrop is always opaque, which can speed up drawing of transparent content and images.
*
* This option was added by request although testing failed to show any measurable performance benefit.
*
* Use with caution. In particular, if the canvas is set to "opaque" (`{alpha: false}`), do _not_ also specify a transparent or translucent color for `grid.properties.backGround` because content may then be drawn with corrupt anti-aliasing (at lest in Chrome v67).
*
* Note that such an "opaque" canvas can still be made to appear translucent using the CSS `opacity` property — a different effect entirely.
*
* Although this option has no apparent performance gains (in Chrome v63), it does permit the graphics context to use [sub-pixel rendering](https://en.wikipedia.org/wiki/Subpixel_rendering) for sharper text as viewed on LCD or LED screens, especially black text on white backgrounds, and especially when viewed on a high-pixel-density display such as an [Apple retina display](https://en.wikipedia.org/wiki/Retina_Display).
*
* value | Canvas | Text | Sample
* ----- | :----: | :--: | ------
* `{ alpha: true } ` | transparent | regular<br>anti-aliasing | ![regular.png](https://cdn-pro.dprcdn.net/files/acc_645730/ZqurK3)
* `{ alpha: false }` | opaque | sub-pixel<br>rendering | ![sub-pixel.png](https://cdn-std.dprcdn.net/files/acc_645730/bf3VXh)
*
* @param {string|Element} [container] - CSS selector or Element. If omitted (and `options.container` also omitted), Hypergrid first looks for an _empty_ element with an ID of `hypergrid`. If not found, it will create a new element. In either case, the container element has the class name `hypergrid-container` added to its class name list. Finally, if the there is more than one such element with that class name, the element's ID attribute is set to `hypergrid` + _n_ where n is an ordinal one less than the number of such elements.
* @param {object} [options] - If `options.data` provided, passed to {@link Hypergrid#setData setData}; else if `options.Behavior` provided, passed to {@link Hypergrid#setBehavior setBehavior}.
* @param {function} [options.Behavior=Local] - _Per {@link Behavior#setData}._
Expand All @@ -73,8 +48,22 @@ var EDGE_STYLES = ['top', 'bottom', 'left', 'right'],
*
* @param {string|Element} [options.container] - Alternative to providing `container` (first) parameter above.
*
* @param {object} [options.canvasContextAttributes] - Passed to [`HTMLCanvasElement.getContext`](https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext). _Please see discussion above._
* @param {object} [options.contextAttributes={ alpha: true }] - Passed to [`HTMLCanvasElement.getContext`](https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement/getContext). Although the MDN docs say setting this to `{alpha: false}` (opaque canvas) can "can speed up drawing of transparent content and images," our testing (with Chrome v63) failed to show any measurable performance gain.
*
* _An opaque canvas does have an important advantage, however!_ It permits the graphics context to use [sub-pixel rendering](https://en.wikipedia.org/wiki/Subpixel_rendering) for sharper text as viewed on LCD or LED screens, especially black text on white backgrounds, and especially when viewed on a high-pixel-density display such as an [Apple retina display](https://en.wikipedia.org/wiki/Retina_Display).
*
* Zoom in on the following samples images to see the difference in rendering.
*
* Value | Sample
* :---: | :----:
* `{ alpha: true }`<br>Transparent canvas,<br>renders text using<br>_regular anti-aliasing_ | ![regular.png](https://cdn-pro.dprcdn.net/files/acc_645730/ZqurK3)
* `{ alpha: false }`<br>Opaque canvas,<br>renders text using<br>_sub-pixel rendering_ | ![sub-pixel.png](https://cdn-std.dprcdn.net/files/acc_645730/bf3VXh).
*
* Use with caution, however. In particular, if the canvas is set to "opaque" (`{alpha: false}`), do _not_ also specify a transparent or translucent color for `grid.properties.backGround` because content may then be drawn with corrupt anti-aliasing (at lest as of Chrome v67).
*
* To clarify, the default setting (`{ alpha: true }`) is a transparent canvas, meaning that elements rendered underneath the `<canvas>` element can be seen through any non-opaque pixels (pixels with alpha channel < 1.0). Hypergrids that set their background color to non-opaque can see this effect.
*
* Note: An opaque canvas can still be made _to appear_ translucent using the CSS `opacity` property. But that is a different effect entirely, setting the entire rendered canvas to translucent, not just so all pixels become translucent.
* @param {string} [options.localization=Hypergrid.localization]
* @param {string|string[]} [options.localization.locale=Hypergrid.localization.locale] - The default locale to use when an explicit `locale` is omitted from localizer constructor calls. Passed to `Intl.NumberFomrat` and `Intl.DateFomrat`. See {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl#Locale_identification_and_negotiation|Locale identification and negotiation} for more information.
* @param {string} [options.localization.numberOptions=Hypergrid.localization.numberOptions] - Options passed to [`Intl.NumberFormat`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat) for creating the basic "number" localizer.
Expand Down Expand Up @@ -1048,7 +1037,11 @@ var Hypergrid = Base.extend('Hypergrid', {

this.div.appendChild(divCanvas);

var canvas = new Canvas(divCanvas, this.renderer, options && options.contextAttributes);
var contextAttributes = options && (
options.contextAttributes ||
options.canvasContextAttributes
);
var canvas = new Canvas(divCanvas, this.renderer, contextAttributes);
canvas.canvas.classList.add('hypergrid');

this.divCanvas = divCanvas;
Expand Down
1 change: 1 addition & 0 deletions src/Hypergrid/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Object.defineProperties(module.exports, {
'datasaur-base': { value: require('datasaur-base') }, // may be removed in a future release
'datasaur-local': { value: require('datasaur-local') }, // may be removed in a future release
'extend-me': {value: require('extend-me') },
finbars: { value: require('finbars') },
'object-iterators': { value: require('object-iterators') },
overrider: { value: require('overrider') },
rectangular: { value: require('rectangular') },
Expand Down
2 changes: 1 addition & 1 deletion src/behaviors/Column.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ Column.prototype = {
},

setWidth: function(width) {
width = Math.max(this.properties.minimumColumnWidth, width);
width = Math.min(Math.max(this.properties.minimumColumnWidth, width), this.properties.maximumColumnWidth || Infinity);
if (width !== this.properties.width) {
this.properties.width = width;
this.properties.columnAutosizing = false;
Expand Down
24 changes: 21 additions & 3 deletions src/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -748,19 +748,37 @@ var defaults = {
defaultRowHeight: version > 2 ? 14 : 15,

/**
* This default column width is used when `width` property is undefined.
* (`width` is defined on column creation unless {@link module:defaults.columnAutosizing columnAutosizing} has been set to `false`.)
* @default
* @type {number}
* @memberOf module:defaults
*/
defaultColumnWidth: 100,

/**
* Minimum column width.
* Adjust this value for different fonts/sizes or exotic cell renderers.
* _Must be defined._
* The default (`5`) is enough room for an ellipsis with default font size.
* @default
* @type {number}
* @memberOf module:defaults
*/
minimumColumnWidth: 5,

/**
* Maximum column width.
* _When defined,_ column width is clamped to this value by {@link Column#setWidth setWidth}).
* Ignored when falsy.
* Respects {@link module:defaults.resizeColumnInPlace resizeColumnInPlace} but may cause user confusion when
* user can't make column narrower due to next column having reached its maximum.
* @default
* @type {number}
* @memberOf module:defaults
*/
maximumColumnWidth: undefined,

/**
* Resizing a column through the UI (by clicking and dragging on the column's
* right border in the column header row) normally affects the width of the whole grid.
Expand All @@ -769,12 +787,12 @@ var defaults = {
* In other words, if user expands (say) the third column, then the fourth column will contract —
* and _vice versa_ — without therefore affecting the width of the grid.
*
* This is a _column propert_ and may be set for selected columns (`myColumn.properties.resizeColumnInPlace`)
* or for all columns by setting it at the grid level. (`myGrid.properties.resizeColumnInPlace`).
* This is a _column property_ and may be set for selected columns (`myColumn.properties.resizeColumnInPlace`)
* or for all columns by setting it at the grid level (`myGrid.properties.resizeColumnInPlace`).
*
* Note that the implementation of this property does not allow expanding a
* column beyond the width it can borrow from the next column.
* The last column, however, is unconstrained and resizing it will affect the total grid width.
* The last column, however, is unconstrained, and resizing of course affects the total grid width.
* @default
* @type {boolean}
* @memberOf module:defaults
Expand Down
26 changes: 16 additions & 10 deletions src/features/ColumnResizing.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,23 @@ var ColumnResizing = Feature.extend('ColumnResizing', {
*/
handleMouseDrag: function(grid, event) {
if (this.dragColumn) {
var delta = this.getMouseValue(event) - this.dragStart;
var dragWidth = this.dragStartWidth + delta;
if (!this.nextColumn) {
var delta = this.getMouseValue(event) - this.dragStart,
dragWidth = this.dragStartWidth + delta,
nextWidth = this.nextStartWidth - delta;
if (!this.nextColumn) { // nextColumn et al instance vars defined when resizeColumnInPlace (by handleMouseDown)
grid.behavior.setColumnWidth(this.dragColumn, dragWidth);
} else if (
0 < delta && delta <= (this.nextStartWidth - this.nextColumn.properties.minimumColumnWidth) ||
0 > delta && delta >= -(this.dragStartWidth - this.dragColumn.properties.minimumColumnWidth)
) {
var nextWidth = this.nextStartWidth - delta;
grid.behavior.setColumnWidth(this.dragColumn, dragWidth);
grid.behavior.setColumnWidth(this.nextColumn, nextWidth);
} else {
var np = this.nextColumn.properties, dp = this.dragColumn.properties;
if (
0 < delta && delta <= (this.nextStartWidth - np.minimumColumnWidth) &&
(!dp.maximumColumnWidth || dragWidth <= dp.maximumColumnWidth)
||
0 > delta && delta >= -(this.dragStartWidth - dp.minimumColumnWidth) &&
(!np.maximumColumnWidth || nextWidth < np.maximumColumnWidth)
) {
grid.behavior.setColumnWidth(this.dragColumn, dragWidth);
grid.behavior.setColumnWidth(this.nextColumn, nextWidth);
}
}
} else if (this.next) {
this.next.handleMouseDrag(grid, event);
Expand Down
95 changes: 74 additions & 21 deletions src/lib/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ Canvas.prototype = {
dispatchNewMouseKeysEvent: function(event, name, detail) {
detail = detail || {};
detail.mouse = this.mouseLocation;
detail.keys = this.currentKeys;
defKeysProp.call(this, event, 'keys', detail);
return this.dispatchNewEvent(event, name, detail);
},

Expand Down Expand Up @@ -452,12 +452,8 @@ Canvas.prototype = {
return;
}

// prevent TAB from moving focus off the canvas element
if (e.keyCode === 9) {
e.preventDefault();
}
var keyChar = updateCurrentKeys.call(this, e, true);

var keyChar = this.getKeyChar(e);
if (e.repeat) {
if (this.repeatKey === keyChar) {
this.repeatKeyCount++;
Expand All @@ -470,11 +466,8 @@ Canvas.prototype = {
this.repeatKeyCount = 0;
this.repeatKeyStartTime = 0;
}
if (this.currentKeys.indexOf(keyChar) === -1) {
this.currentKeys.push(keyChar);
}

this.dispatchNewEvent(e, 'fin-canvas-keydown', {
this.dispatchNewEvent(e, 'fin-canvas-keydown', defKeysProp.call(this, e, 'currentKeys', {
alt: e.altKey,
ctrl: e.ctrlKey,
char: keyChar,
Expand All @@ -485,27 +478,21 @@ Canvas.prototype = {
repeatCount: this.repeatKeyCount,
repeatStartTime: this.repeatKeyStartTime,
shift: e.shiftKey,
identifier: e.key,
currentKeys: this.currentKeys.slice(0)
});
identifier: e.key
}));
},

finkeyup: function(e) {
if (!this.hasFocus()) {
return;
}

// prevent TAB from moving focus off the canvas element
if (e.keyCode === 9) {
e.preventDefault();
}
var keyChar = updateCurrentKeys.call(this, e, false);

var keyChar = this.getKeyChar(e);
this.currentKeys.splice(this.currentKeys.indexOf(keyChar), 1);
this.repeatKeyCount = 0;
this.repeatKey = null;
this.repeatKeyStartTime = 0;
this.dispatchNewEvent(e, 'fin-canvas-keyup', {
this.dispatchNewEvent(e, 'fin-canvas-keyup', defKeysProp.call(this, e, 'currentKeys', {
alt: e.altKey,
ctrl: e.ctrlKey,
char: keyChar,
Expand All @@ -517,7 +504,7 @@ Canvas.prototype = {
shift: e.shiftKey,
identifier: e.key,
currentKeys: this.currentKeys.slice(0)
});
}));
},

finfocusgained: function(e) {
Expand Down Expand Up @@ -791,6 +778,72 @@ function makeCharMap() {
return map;
}

function updateCurrentKeys(e, keydown) {
var keyChar = this.getKeyChar(e);

// prevent TAB from moving focus off the canvas element
switch (keyChar) {
case 'TAB':
case 'TABSHIFT':
case 'Tab':
e.preventDefault();
}

fixCurrentKeys.call(this, keyChar, keydown);

return keyChar;
}

function fixCurrentKeys(keyChar, keydown) {
var index = this.currentKeys.indexOf(keyChar);

if (!keydown && index >= 0) {
this.currentKeys.splice(index, 1);
}

if (keyChar === 'SHIFT') {
// on keydown, replace unshifted keys with shifted keys
// on keyup, vice-versa
this.currentKeys.forEach(function(key, index, currentKeys) {
var pair = charMap.find(function(pair) {
return pair[keydown ? 0 : 1] === key;
});
if (pair) {
currentKeys[index] = pair[keydown ? 1 : 0];
}
});
}

if (keydown && index < 0) {
this.currentKeys.push(keyChar);
}
}

function defKeysProp(event, propName, object) {
var canvas = this;
Object.defineProperty(object, propName, {
configurable: true,
ennumerable: true,
get: function() {
var shiftKey;
if ('shiftKey' in event) {
fixCurrentKeys.call(canvas, 'SHIFT', shiftKey = event.shiftKey);
} else {
shiftKey = canvas.currentKeys.indexOf('SHIFT') >= 0;
}
var SHIFT = shiftKey ? 'SHIFT' : '';
if ('ctrlKey' in event) {
fixCurrentKeys.call(canvas, 'CTRL' + SHIFT, event.ctrlKey);
}
if ('altKey' in event) {
fixCurrentKeys.call(canvas, 'ALT' + SHIFT, event.altKey);
}
return canvas.currentKeys.slice();
}
});
return object;
}

function getCachedContext(canvasElement, contextAttributes) {
var gc = canvasElement.getContext('2d', contextAttributes),
props = {},
Expand Down
Loading

0 comments on commit bfb75d5

Please sign in to comment.