Skip to content

Release Notes: v2.1.0

Jonathan Eiten edited this page Apr 11, 2018 · 2 revisions

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.

Minor version upgrade

This is a minor version upgrade which means there should be no breaking changes.

Deprecations

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.

Bug fixes

  • 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, and subgrids.

New Properties

Distinct grid line styling

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 and gridLinesV.
  • The default values for these properties are functionally equivalent to the default values for the legacy lineWidth and lineColor properties.
  • These legacy lineWidth and lineColor properties, which these new properties are intended to replace, are still supported and function as follows:
    • lineWidth sets both gridLinesHWidth and gridLinesVWidth to the same value but gets only gridLines.horizontal.width.
    • lineColor sets both gridLinesHColor and gridLinesVColor to the same value but gets only gridLines.horizontal.color.

Default:

    gridLinesHWidth: 1,
    gridLinesVWidth: 1,
    gridLinesHColor: 'rgb(199, 199, 199)'
    gridLinesVColor: 'rgb(199, 199, 199)'

Fixed rows and columns boundary lines

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 and fixedLinesVWidth — Boundary line thickness.
  • fixedLinesHEdge and fixedLinesVEdge — Boundary line edge rendering (off by default).
  • fixedLinesHColor and fixedLinesVColor — Boundary line color.

Notes:

  • The defaults for these property are not consistent with the defaults for the legacy lineWidth and lineColor 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.

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

Show/hide individual row header column features

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 the checkboxes, 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 both rowHeaderCheckboxes and rowHeaderNumbers
    • Getting showRowNumbers returns truthy if either is truthy.

Default:

    rowHeaderCheckboxes: true,
    rowHeaderNumbers: true

gridBorder — Set all grid borders at once

  • This sets all four of the existing gridBorderTop, gridBorderTop, gridBorderTop, and gridBorderTop properties.
  • Value is overloaded:
    • boolean:
      • truecanvas.style.border = <lineWidth>px solid <lineColor>
      • false or nullcanvas.style.border = null
    • string: An arbitrary CSS border shorthand spec.
  • 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.

features — serializable feature chain property

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 the Hypergrid constructor
      • Is suitable for loading with Hypergrid#loadState

New methods

New Behavior.prototype methods

Behavior#getRowHeaderColumn

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).

New Renderer.prototype methods

Renderer#assignProps

See Regarding Performance (below).

Renderer#resetRowHeaderColumnWidth

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.

Retired methods

The following methods have been removed. They had no apparent logical purpose and were not being called by Hypergrid itself.

  • Hypergrid#setScrollingNow
  • Hypergrid#isScrollingNow

External Pull Requests

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

Other changes

In no particular order:

Linter

Registries

  • The ./src/cellEditors and ./src/cellRenderers registry classes now extend from a common Registry 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 object items on the registry instance. (However, Hypergrid currently does not support creating the registries in this way.)

Demo event loggers

  • Pulled out event loggers (console.log calls made in event listeners when demo.vent is set) and moved them to a new repo fin-hypergrid/event-logger (uses the starlog npm module).

package-lock.json file

The repo now includes this file for precise builds per npmjs.org.

Row properties

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.

As metadata

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.

In state object

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.

Programmatic support

The following API has been added in support of row properties in Behavior.prototype.

getRowProperties(yOrCellEvent, properties, dataModel)

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 (where data is the subgrid name).
  • undefined if the row properties object did not exist and you did not provide a value in properties.rows.
setRowProperties(yOrCellEvent, properties, dataModel)

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).
setRowProperty(yOrCellEvent, key, value, dataModel)

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).
addRowProperties(yOrCellEvent, properties, dataModel)

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 property formerly known as rowProperties

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.

Property layers

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 as rowProperties; 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
];

Regarding performance

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).

Double-click delay removed to make single-clicks more responsive

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.

Defunct option: doubleClickDelay

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.

New option: sortOnDoubleClick

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.

Overridable modules

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 requireing 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.

Lazified certain selection event properties

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

Defer "changed" methods

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.

treeColumnIndex and rowColumnIndex

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.]

getRowMetadata and setRowMetadata

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.

Grid Borders

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.

Clone this wiki locally