-
Notifications
You must be signed in to change notification settings - Fork 143
Release Notes: v2.1.0
Welcome to Hypergrid 2.1.0.
This is an interim release before the next major upgrade, 3.0.0, due out by end of this month (January, 2018).
The following is a (hopefully) comprehensive list of bug fixes and new features.
This is a minor version upgrade which means there should be no breaking changes.
Some method calls and property access have been deprecated. Although the legacy feature is still supported (for now), a one-time (per app execution) deprecation warning will be output to the console advising the developer of the new (and preferred) method call or property access. This should give developers a window for updating their code. Most of these deprecation warnings state that legacy support will be "removed in a future version" so developers take heed! The window will generally be at least a year but this is not guaranteed.
- Formerly, SHIFT key modifier was not always being properly included in event objects. This has been fixed.
- Formerly,
fin-mouseup
event was being issued to all grid instances on the page, rather than the one under the mouse at the time. This has been fixed. - Make sure canvas size is integral. When using hypergrid in a layout engine (like phosphor or golden-layout), sometimes these engines give the canvas a non-integral width and height, which causes text aliasing issues since canvas support sub-pixel referencing. This fix just floors the bounds of the canvas element in a consistent way.
- Fire row selection event when auto selecting rows. Original code was not triggering the event in this case.
- Fixed issue in IE with I-beam cursor. Not sure why content editable was ever set on a canvas element, but in IE it causes the cursor to always be the I-beam shape. We removed this line entirely, to no apparent ill effect. This fixes the first part of issue #617.
- Row properties are now applied before rather than after cell properties on render. (The order in which properties is applied can now be set via a new property
propLayers
described below.) - Don't do anything if there's no data. Formerly, if the grid had no data, mouse event handler logic was throwing spurious console errors as the user mouses over the empty grid. This has been fixed.
- Fixed a bug where rendering was corrupted if row numbers column was hidden while there is a tree column.
- Grids are now automatically given distinct copies of complex properties so that changing the inner properties won't affect all instances. Complex properties are object containing nested properties. There is a known bug with these props which is that they have priority over values in themes. The current complex properties are
hoverCellHighlight
,hoverCellHighlight
,hoverCellHighlight
,navKeyMap
,propClassLayers
,features
, andsubgrids
.
These new properties add the ability to independently style horizontal and vertical rule lines' color and width (thickness):
-
gridLinesHWidth
— Width of horizontal grid lines. -
gridLinesVWidth
— Width of vertical grid lines. -
gridLinesHColor
— Color of horizontal grid lines. -
gridLinesVColor
— Color of vertical grid lines.
Notes:
- These are adjuncts to the existing boolean "enable" properties,
gridLinesH
andgridLinesV
. - The default values for these properties are functionally equivalent to the default values for the legacy
lineWidth
andlineColor
properties. - These legacy
lineWidth
andlineColor
properties, which these new properties are intended to replace, are still supported and function as follows:-
lineWidth
sets bothgridLinesHWidth
andgridLinesVWidth
to the same value but gets onlygridLines.horizontal.width
. -
lineColor
sets bothgridLinesHColor
andgridLinesVColor
to the same value but gets onlygridLines.horizontal.color
.
-
Default:
gridLinesHWidth: 1,
gridLinesVWidth: 1,
gridLinesHColor: 'rgb(199, 199, 199)'
gridLinesVColor: 'rgb(199, 199, 199)'
This is a new feature, the ability to specify distinct width and color for the gird rule line that separates fixed and scrolling rows and columns.
As with the above "gridLines" properties, these new properties also have distinct "H" (horizontal) and "V" (vertical) versions:
-
fixedLinesHWidth
andfixedLinesVWidth
— Boundary line thickness. -
fixedLinesHEdge
andfixedLinesVEdge
— Boundary line edge rendering (off by default). -
fixedLinesHColor
andfixedLinesVColor
— Boundary line color.
Notes:
- The defaults for these property are not consistent with the defaults for the legacy
lineWidth
andlineColor
properties. The new defaults instead specify:- 2-pixel-wide rule lines (rather than 1-pixel)
- slightly darker shade of grey (21% darker)
- Fallbacks: If all four properties are explicitly
undefined
, you will get the legacy appearance, because:- If
fixedLinesHWidth === undefined
,gridLinesHWidth
is used instead. - If
fixedLinesVWidth === undefined
,gridLinesVWidth
is used instead. - If
fixedLinesHColor === undefined
,gridLinesHColor
is used instead. - If
fixedLinesVColor === undefined
,gridLinesVColor
is used instead.
- If
Default:
fixedLinesHWidth: 2,
fixedLinesVWidth: 2,
fixedLinesHEdge: undefined, // undefined means no edge effect
fixedLinesVEdge: undefined, // undefined means no edge effect
fixedLinesHColor: 'rgb(164,164,164)' // ~21% darker than `gridLinesHColor` default
fixedLinesVColor: 'rgb(164,164,164)' // ~21% darker than `gridLinesVColor` default
These properties are the successors to the legacy property showRowNumbers
(which is still supported; see note below):
-
rowHeaderCheckboxes
— Turn checkbox rendering on and off. -
rowHeaderNumbers
— Turn row index number rendering on and off.
Notes:
- The default values for these properties are consistent with the default value for the legacy property (which was
showRowNumbers: true
). That is, the column is rendered with both checkboxes and row index numbers. - If you hide both the row
numbers
or thecheckboxes
, the column is not rendered at all. - Otherwise the setters force column width recalculation before the next render.
- The legacy
showRowNumbers
property is still supported and functions as follows:- Setting
showRowNumbers
sets bothrowHeaderCheckboxes
androwHeaderNumbers
- Getting
showRowNumbers
returns truthy if either is truthy.
- Setting
Default:
rowHeaderCheckboxes: true,
rowHeaderNumbers: true
- This sets all four of the existing
gridBorderTop
,gridBorderTop
,gridBorderTop
, andgridBorderTop
properties. - Value is overloaded:
-
boolean:
-
true
—canvas.style.border = <lineWidth>px solid <lineColor>
-
false
ornull
—canvas.style.border = null
-
- string: An arbitrary CSS border shorthand spec.
-
boolean:
- All four of the existing properties, which formerly accepted only a boolean, now accept the above overloads as well.
- Important: See Grid Borders (below) regarding new grid border property implementation.
The feature chain was originally built from the list of feature constructors found in the Behavior#features
array (which your behavior needed to define). There is now a new features
grid property, a serializable version of the Behavior#features
, consisting of registered case-insensitive feature names that is persisted with grid state (as opposed to references to feature constructors).
If this new features
property is defined, it will get priority over Behavior#features
— which is the case by default because there is now a definition in Hypergrid.defaults
consisting of the same list of features formerly in Behavior#features
(but using registered feature names rather than references to their constructors). This new features
property will therefore be used — even if you have also defined behavior.features
.
That said, to continue to use behavior.features
array as before (which we don't recommend), delete the features
property (or set it to undefined
) and define the Behavior#features
array:
delete Hypergrid.defaults.features;
Behavior.prototype.features = myListOfFeatureConstructors;
Be aware however that you will receive a one-time (per app execution) "deprecation" warning in the console. This warning includes sample code for a features
grid property definition with which to replace it.
Note also the following regarding the features
grid property:
- The names represented by the strings in the array must be registered prior to being referenced.
- Features may be registered at any time by calling the static method
Features.add(myFeature)
which registers features in the global feature registry (available to all grid instances).. - The
features
property is dynamic, rebuilding the feature chain whenever it is set. - As a grid property (quick review):
- It is serializable.
- It has a default value in
Hypergrid.defaults
. - It may appear in a serializable grid state object:
- Which may be passed as the
state
option to theHypergrid
constructor - Is suitable for loading with
Hypergrid#loadState
- Which may be passed as the
Returns the column object of the row header column, i.e., this.allColumns[this.rowColumnIndex]
.
Behavior#getRowProperties(yOrCellEvent, properties, dataModel)
Behavior#setRowProperties(yOrCellEvent, properties, dataModel)
Behavior#setRowProperty(yOrCellEvent, key, value, dataModel)
Behavior#addRowProperties(yOrCellEvent, properties, dataModel)
See Row Properties (below).
See Regarding Performance (below).
Recalculates the row header column width based on current visibility of row checkboxes and row numbers and if and only if the width has changed, sets a flag to recompute cell boundaries prior to next render.
The following methods have been removed. They had no apparent logical purpose and were not being called by Hypergrid itself.
Hypergrid#setScrollingNow
Hypergrid#isScrollingNow
Please refer to the following pull requests from external contributors, previously merged into develop:
- #598 — Fix keyboard navigation
- #646 — Fixed canvas key event map typo
In no particular order:
- Updated to
[email protected]
- The
./src/cellEditors
and./src/cellRenderers
registry classes now extend from a commonRegistry
base class. -
./src/features
now has a registry as well. - Each of these subclasses now has a
BaseClass
property that points to its respective base class so that it does not itself need to be registered just to be accessible (especially when the base class is abstract and therefore unusable by itself). - Each of these subclasses now has a static method
add
to register items in the respective global registries, which means they would be available to all grid instances. -
Registry
supports "private" registries for individual grid instances simply by defining a plain objectitems
on the registry instance. (However, Hypergrid currently does not support creating the registries in this way.)
- Pulled out event loggers (
console.log
calls made in event listeners whendemo.vent
is set) and moved them to a new repofin-hypergrid/event-logger
(uses thestarlog
npm module).
The repo now includes this file for precise builds per npmjs.org.
We now have a full row properties implementation. That is, specific individual rows can now have their own properties objects.
Before jumping on row properties, consider using row stripe properties which may cover your use case. They are simpler to deal with and typically more performant than setting up individual row properties objects.
Row properties, like cell properties, are implemented as row metadata, and can in theory be persisted with the data, which could potentially be the most practical (as well as the most efficient) way of persisting these properties. This can be especially advantageous for dynamic grids that are constantly showing new row data. See the Row and cell properties as metadata wiki for more information about how to do this.
Row properties can alternatively be saved and reloaded with the serializable grid state object (passed in the Hypergrid
constructor's options object in its state
property; or passed to `grid.loadState({ state: { rows: {...} }). The use case for this approach is static grids that don't change their row data. In this case, the application is responsible for persisting these properties.
The following API has been added in support of row properties in Behavior.prototype
.
Reset the row properties to the given row properties object.
# | Formal Param |
Type | Default when omitted |
Description |
---|---|---|---|---|
1 |
y or cellEvent
|
number or CellEvent
|
(required) | Data row index local to dataModel or A CellEvent object. |
2 | properties | object | — | New properties object when one does not already exist. If you don't provide this and one does not already exist, this call will return undefined . (Required when 3rd param provided.)
|
3 | dataModel |
dataModelAPI (this is a jsdoc typedef) |
this.dataModel |
This is the subgrid. You only need to provide the subgrid when it is not the data subgrid and you did not give a CellEvent object in the first param (which already knows what subgrid it's in). |
Returns the row properties object which will be one of:
- The row properties object if it existed.
- The value you provided for the row in the
properties.rows.data.<row-number>
grid property (wheredata
is the subgrid name). -
undefined
if the row properties object did not exist and you did not provide a value inproperties.rows
.
Reset the row properties in its entirety to the given row properties object.
# | Formal Param |
Type | Default when omitted |
Description |
---|---|---|---|---|
1 |
y or cellEvent
|
number or CellEvent
|
(required) | Data row index local to dataModel or A CellEvent object. |
2 | properties | object | (required) | The new row properties object. |
3 | dataModel |
dataModelAPI (this is a jsdoc typedef) |
this.dataModel |
This is the subgrid. You only need to provide the subgrid when it is not the data subgrid and you did not give a CellEvent object in the first param (which already knows what subgrid it's in). |
Sets a single row property on a specific individual row.
# | Formal Param |
Type | Default when omitted |
Description |
---|---|---|---|---|
1 |
y or cellEvent
|
number or CellEvent
|
(required) | Data row index local to dataModel or A CellEvent object. |
2 | key | string | (required) | The property name. |
3 | key | any | (required) | The new property value. |
4 | dataModel |
dataModelAPI (this is a jsdoc typedef) |
this.dataModel |
This is the subgrid. You only need to provide the subgrid when it is not the data subgrid and you did not give a CellEvent object in the first param (which already knows what subgrid it's in). |
Add all the properties in the given row properties object to the row properties.
# | Formal Param |
Type | Default when omitted |
Description |
---|---|---|---|---|
1 |
y or cellEvent
|
number or CellEvent
|
(required) | Data row index local to dataModel or A CellEvent object. |
2 | properties | object | (required) | An object containing new property values(s) to assign to the row properties. |
3 | dataModel |
dataModelAPI (this is a jsdoc typedef) |
this.dataModel |
This is the subgrid. You only need to provide the subgrid when it is not the data subgrid and you did not give a CellEvent object in the first param (which already knows what subgrid it's in). |
The rowProperties
property which has been deprecated in favor of a better name rowStripes
. See Deprecations (above). Now that we have actual row properties, we felt the legacy name would just lead to a lot of confusion.
The term property layers refers to the order in which the properties of cells, rows, and columns are layered over the grid properties.
Formerly, the property object hierarchy was searched in this order (reading right to left):
(grid properties) ← column properties ← cell properties ← row stripe properties
A new property called propLayers
(of a cell, a column, or of the whole grid) specify, for a given cell render, the order in which properties should be applied. While grid properties always come last (which is why grid is in parentheses above), the other properties can now be applied in an arbitrary order.
A note on row properties: The term "formerly" above refers to the limited former support for row height and the properties objects that comprise the
rowStripes
array (formerly known asrowProperties
; see The property formerly known as rowProperties above for more information).
Now that we have the new full row properties implementation for individual specific rows (see Row Properties below), there are now two distinct types of row-oriented properties objects, which are applied separately: The row stripes objects and the individual row properties objects.
The default order is:
(grid properties) ← column properties ← row stripe properties ← row properties ← cell properties
The layers are referenced in a new property, propClassLayers
, which is an array of integers. (We first tried an array of strings, but it noticeably impacted performance, due to the cost of string compares which are much more complicated than integer compares (which are a single machine-level instruction) at this very low-level, accessed repeatedly and constantly.
For the developer's convenience, the integers are available in an enum which itself is a grid property (just to make it universally available wherever grid properties are available) called propClassEnum
:
var propClassEnum = {
COLUMNS: 1,
STRIPES: 2,
ROWS: 3,
CELLS: 4
};
To set the layers (using the default order as an example):
var propClassEnum = grid.properties.propClassEnum;
grid.properties.propClassLayers = [
propClassEnum.COLUMNS,
propClassEnum.STRIPES,
propClassEnum.ROWS,
propClassEnum.CELLS
];
Altering the propClassLayers
array from it's default order should be used with caution, however, when performance is a factor.
The fact that row properties were previously overwriting cell properties by default reflected the way cell properties objects are implemented: Like column properties objects, which inherit from the grid properties object, cell properties objects inherit from the column properties object of the column in which they reside.
This design makes use of prototypal inheritance which, as native code, it is very performant. We are of course subject to the various (and often mysterious) internal optimizations made by the various browsers' JavaScript engines, but generally speaking there are significant performance advantages to designing the code to use prototypal inheritance for these properties objects.
Applying the properties out of order, however, cannot take advantage of this and instead requires copying the properties. Row properties objects in particular, which have no prototype, must always be copied in at render time.
In testing however (theory aside), the only significant drop in performance seemed to be when column properties were not listed first (after grid). We intend to keep experimenting with this to get the best performance. To this end, the critical logic for extracting the properties from the properties hierarchy has been isolated in a new overridable method (Renderer#assignProps
).
We previously waited for a double-click delay before triggering fin-click
. We have removed this in favor of using the system dblclick
event. This makes all single-clicks more responsive.
The doubleClickDelay
property has been removed. The double-click delay is no longer defined in properties. Trying to set it results in a warning that it is now ineffective and the double-click delay is now system-dependent (e.g., in a mouse control panel).
For the record, the defunct property defaulted to 325 msec.
Also added an option to source the triggering of the fin-column-sort
event to either single-click or double-click (previously was hard-coded to double-click). The new property is sortOnDoubleClick
, which parallels the naming convention established by the existing editOnDoubleClick
property. The
default value is true
meaning double-click (which is consistent with the legacy behavior); change this to false
for single-click.
When false
, both sorting and column selection toggling occur on the same click.
When true
(sort is double-click), we have the very situation warned against on the web:
It is inadvisable to bind handlers to both the click and dblclick events for the same element. ...
In this case (and only in this particular case) what we do is delay before responding to single click (for column selection). This is the only double-click delay remaining in the code base.
We cannot unfortunately read the system double-click delay so we have hard-coded the above delay to 300 msec.
Another change is that cell editing now respects cell's or column's property setting rather than relying exclusively on grid property setting.
Hypergrid uses an external module for scroll bar support. This module, finbars
can now be easily overridden by replacing Hypergrid.modules.Scrollbars
with a new class (constructor function) that conforms to the same interface as finbars
.
From the comment at the top of the new modules.js
, the collection of overridable modules:
This module is the namespace of loaded external modules known to
Hypergrid.require
, which may include loaded application modules, datasource modules, and plug-in modules.The pre-loaded external modules listed below can conveniently be overridden by the application developer by loading a new module using the same key.
For example, to override
finbars
with another compatible module (that conforms to the same interface), just assign it like so:Hypergrid.modules.Scrollbar = myFinbarReplacement;
For these "overridable" modules specifically, the idea is that Hypergrid references these modules through Hypergrid.modules
rather than by require
ing them directly. However, it is worth noting that Hypergrid.modules
is a general collection that eventually includes all loaded external modules; the collection of "overridable' modules defined in modules.js
is just the initial collection before any external modules are loaded.
Certain properties on some selection event objects are costly to run and have been converted to getters so they are not run at event dispatch time but are only run by the event handler and only when actually called for.
Even though these were only invoked rarely (on specific user interactions), they could be very costly if a lot of rows/columns are involved, in terms of memory and execution time.
The properties in question are as follows:
rows
columns
selections
These appear in the event objects of the following events:
fin-row-selection-changed
fin-column-selection
fin-context-menu
fin-mouseup
fin-mousedown
The following methods can now be called multiple times, similar to repaint()
, without worrying about repeating lengthy recalculation algorithms. All such calls are now collapsed and deferred until the end of the thread:
-
behaviorChanged()
, which does one of the following: -
behaviorShapeChanged()
, which recalculates scrollbars and is always followed by a call to... -
behaviorStateChanged()
, which recalculate rects of visible cells and is always followed by a call to… -
repaint()
(this one was already being deferred by setting a flag that was inspected at render time)
In addition, calls to Renderer#computeCellBounds
now collapse and defer that computation until next render time.
In the past, we have always tried only to make such calls near end of thread, sometimes omitting calls where logically needed just in order to avoid multiple recalcs but sometimes no doubt incurring multiple recalcs needlessly. Now, however, these methods can be called multiple times wihtout incurring this burden. (Caution should still be exercised however to avoid calling for a higher level of recalculation than necessary.)
Note that certain idiosyncratic cases that previously required using setTimeout
to position calls at end-of-thread are no longer necessary and some of these have been removed.
These constants are no longer included in the call to the data source constructor (as the 3rd and 4th parameters). [This change reverted as of v2.1.1.]
Data models must now implement these methods. This is generally only a concern if you have custom data models that do not extend from DataModel
, in which case you will need to implement these methods (for example, by copying them in from dataModels/DataModel.js
, or supplying your own).
Note: Even if you don't need to support metadata (i.e., row and cell properties), you still need to define stubs (that do nothing).
Note: This will not actually be a concern in Hypergrid 3.0.0 (to be released later this month), which specifies that data sources (as opposed to data models) should implement these. However, if they do not, Hypergrid supplies "fallbacks" (default implementations), which is why it will not be a concern.
The implementation of the rarely used grid border properties has changed. Previously, borders were painted into the canvas. This has changed. The implementations of the various grid border properties (the new gridBorder
property as well as the existing gridBorderTop
, gridBorderTop
, gridBorderTop
, and gridBorderTop
properties) now instead set the CSS borders of the <canvas>
element. The borders are now rendered by CSS on the outside of the canvas.
Although you can set canvas.style.box-sizing
to border-box
(instead of the default content-box
) to render the borders on the inside of the canvas element, this only has the effect of sizing the canvas that much smaller to make room for the borders. Because Hypergrid currently sizes the <canvas>
element to the size of its container, without considering the box model, the result is a slightly squashed canvas, which blurs the text in the grid, so this is very definitely not recommended! We might try to address this in a future update but for now just be aware that if you use these border properties, the borders will now be drawn around the outside of the <canvas>
element, not within it.
The reason we kept these properties at all was so that their settings could be persisted with grid state along with the other look & feel properties that are painted onto the canvas. However, if this feature is not important to you, you don't have to use it. Just set the borders in your CSS stylesheet instead.