Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/finos/regular-table
A regular <table> library, for async and virtual data models.
https://github.com/finos/regular-table
data-visualization javascript jpmorganchase table
Last synced: about 2 months ago
JSON representation
A regular <table> library, for async and virtual data models.
- Host: GitHub
- URL: https://github.com/finos/regular-table
- Owner: finos
- License: apache-2.0
- Created: 2020-05-22T17:57:59.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-10-26T03:15:04.000Z (2 months ago)
- Last Synced: 2024-11-11T18:11:54.871Z (about 2 months ago)
- Topics: data-visualization, javascript, jpmorganchase, table
- Language: JavaScript
- Homepage:
- Size: 3.83 MB
- Stars: 350
- Watchers: 18
- Forks: 38
- Open Issues: 14
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: .github/CODE_OF_CONDUCT.md
- Authors: AUTHORS
Awesome Lists containing this project
- awesome-list - regular-table
README
#
A Javascript library for the browser, `regular-table` exports a
[custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)
named ``, which renders a regular HTML `` to a `sticky`
position within a scollable viewport. Only visible cells are rendered and
queried from a natively `async` virtual data model, making `regular-table` ideal
for enormous or remote data sets. Use it to build Data Grids, Spreadsheets,
Pivot Tables, File Trees, or anytime you need:- Just a regular ``.
- Virtually rendered for high-performance.
- `async` data model handles slow, remote, enormous, and/or distributed
backends.
- Easy to style, works with any regular CSS for ``.
- Small bundle size, no dependencies.## Examples
- [2d_array.md](examples/2d_array.md)
- [canvas_data_model.md](examples/canvas_data_model.md)
- [file_browser.md](examples/file_browser.md)
- [minesweeper.md](examples/minesweeper.md)
- [react.md](examples/react.md)
- [spreadsheet.md](examples/spreadsheet.md)
- [two_billion_rows.md](examples/two_billion_rows.md)## Documentation
What follows functions as a quick-start guide, and will explain the basics of
the Virtual Data Models, Styling and Interaction APIs. Complete
[API docs](https://github.com/finos/regular-table/blob/master/api.md) and
documented
[examples](https://github.com/finos/regular-table/tree/master/examples) are also
available.- QuickStart
- [Installation](#installation)
- [`` Custom Element](#regular-table-custom-element)
- [`.setDataListener()` Virtual Data Model](#setdatalistener-virtual-data-model)
- [Column and Row Headers](#column-and-row-headers)
- [Hierarchial/Group Headers](#hierarchialgroup-headers)
- [`async` Data Models](#async-data-models)
- [`.addStyleListener()` and `getMeta()` Styling](#addstylelistener-and-getmeta-styling)
- [`.invalidate()`](#invalidate)
- [`.addEventListener()` Interaction](#addeventlistener-interaction)
- [Scrolling](#scrolling)
- [Pivots, Filters, Sorts, and Column Expressions with `perspective`](#pivots-filters-sorts-and-column-expressions-with-perspective)
- [Development](#development)- [API Docs](https://github.com/finos/regular-table/blob/master/api.md)
- Annotated Examples
- [2d_array.md](examples/2d_array.md)
- [canvas_data_model.md](examples/canvas_data_model.md)
- [file_browser.md](examples/file_browser.md)
- [minesweeper.md](examples/minesweeper.md)
- [react.md](examples/react.md)
- [spreadsheet.md](examples/spreadsheet.md)
- [two_billion_rows.md](examples/two_billion_rows.md)## Installation
Include via a CDN like [JSDelivr](https://cdn.jsdelivr.net/npm/regular-table):
```html
```
Or, add to your project via `yarn`:
```bash
yarn add regular-table
```... then import into your asset bundle.
```javascript
import "regular-table";
import "regular-table/dist/css/material.css";
```## `` Custom Element
`regular-table` exports no symbols, only the `` Custom Element
which is registered as a module import side-effect. Once loaded,
`` can be used just like any other `HTMLElement`, using regular
browser APIs:```javascript
const regularTable = document.createElement("regular-table");
document.body.appendChild(regularTable);
```... or from regular HTML:
```html
```
... or from your library of choice, as long as it supports regular HTML! Here's
an example for [React/JSX](https://reactjs.org/):```javascript
const App = () => ;
ReactDOM.render(, document.getElementById("root"));
```## `.setDataListener()` Virtual Data Model
Let's start with with a simple data model, a two dimensional `Array`. This one
is very small at 3 columns x 6 rows, but even for very small data sets,
`regular-table` won't read your entire dataset at once. Instead, we'll need to
write a simple _virtual_ data model to access `DATA` and `COLUMN_NAMES`
indirectly.```javascript
const DATA = [
[0, 1, 2, 3, 4, 5],
["A", "B", "C", "D", "E", "F"],
[true, false, true, false, true, false],
];
```When clipped by the scrollable viewport, you may end up with a `` of just
a rectangular region of `DATA`, rather than the entire set. A simple viewport
2x2 may yield this ``:0
A1
B```json
{
"num_rows": 26,
"num_columns": 3,
"data": [
[0, 1],
["A", "B"]
]
}
```Here's a an implementation for this simple _virtual_ data model, the function
`getDataSlice()`. This function is called by your `` whenever it
needs more data, with coordinate arguments, `(x0, y0)` to `(x1, y1)`. Only this
region is needed to render the viewport, so `getDataSlice()` returns this
rectangular `slice` of `DATA`. For the window (0, 0) to (2, 2), `getDataSlice()`
would generate an Object as above, containing the `data` slice, as well as the
overall dimensions of `DATA` itself ( `num_rows`, `num_columns`), for sizing the
scroll area. To render this virtual data model to a regular HTML ``,
register this data model via the `setDataListener()` method:```javascript
function getDataSlice(x0, y0, x1, y1) {
return {
num_rows: (num_rows = DATA[0].length),
num_columns: DATA.length,
data: DATA.slice(x0, x1).map((col) => col.slice(y0, y1)),
};
}regularTable.setDataListener(getDataSlice);
```This will render your regular HTML `` ! Your DOM will look something like
this, depending on the size of your viewport. Notice there are fewer rows and
columns in the resulting HTML, e.g. the column `Column 3 (boolean)` - as you
scroll, more data will be fetched from `getDataSlice()`, and parts of the
`` will redrawn or extended as needed.```html
0
A
1
B
```
#### `virtual_mode` Option
`regular-table` supports four modes of virtual scrolling, which can be
configured via the `virtual_mode` optional argument. Note that using a
`virtual_mode` other than the default `"both"` will render the _entire_
`` along the non-virtual axis(es), and may cause rendering performance
degradation.- "both" (default) virtualizes scrolling on both axes.
- "vertical" only virtualizes vertical (y) scrolling.
- "horizontal" only virtualizes horizontal (x) scrolling.
- "none" disable all scroll virtualization.```javascript
table.setDataListener(listener, { virtual_mode: "vertical" });
```### Column and Row Headers
`regular-table` can also generate Hierarchial Row and Column Headers, using
`` elements which layout in a `fixed` position within the virtual table. It
can generate Column Headers (within the ``), or Row Headers (the first
children of each `tbody tr`), via the `column_headers` and `row_headers`
properties (respectively) of your data model's `Response` object. This can be
renderered with `column_headers`, a two dimensional `Array` which must be of
length `x1 - x0`, one `Array` for every column in your `data` window.Column 1 (number)
Column 2 (string)0
A1
B```json
{
"num_rows": 26,
"num_columns": 3,
"data": [
[0, 1],
["A", "B"]
],
"column_headers": [["Column 1 (number)"], ["Column 2 (string)"]]
}
```### Hierarchial/Group Headers
`regular-table` supports multiple `` of ``, and also uses `colspan` and
`rowspan` to merge simple consecutive names, which allows description of simple
Row and Column Group Hierarchies such as this:Colgroup 1
Column 1
Column 2Rowgroup 1
Row 1
0
ARow 2
1
B```json
{
"num_rows": 26,
"num_columns": 3,
"data": [
[0, 1],
["A", "B"]
],
"row_headers": [
["Rowgroup 1", "Row 1"],
["Rowgroup 1", "Row 2"]
],
"column_headers": [
["Colgroup 1", "Column 1"],
["Colgroup 1", "Column 2"]
]
}
```Note that in the rendered HTML, for these Row and Column `Array`, repeated
elements in a sequence will be automatically merged via `rowspan` and `colspan`
attributes. In this example, e.g. `"Rowgroup 1"` will only output to one ``
node in the resulting ``.Header merging can be disabled with the `merge_headers` option.
### `metadata` Data-Aware Styling
A `dataListener` may also optionally provide a `metadata` field in its response,
a two dimensional `Array` of the same dimensions as `data`. The values in this
field will accompany the metadata records returned by `regular-table`'s
`getMeta()` method (as described in the next section).```json
{
"num_rows": 26,
"num_columns": 3,
"data": [
[-1, 1],
["A", "B"]
],
"metadata": [
["pos", "neg"],
["green", "red"]
]
}
```### Rendering Options
Additional rendering options which can be set on the object returned by a
`setDataListener` callback include:* `column_header_merge_depth: number` configures the number of rows to include
from `colspan` merging. This defaults to `header_length - 1`.
* `row_height: number` configures the pixel height of a row for
virtual scrolling calculation. This is typically auto-detected from the DOM,
but can be overridden if needed.
* `merge_headers: "column" | "row" | "both" | "none"` configures whether
equivalent, contiguous `` elements are merged via `rowspan` or `colspan`
for `"row"` and `"column"` respectively (defaults to `"both"`).### `async` Data Models
With an `async` data model, it's easy to serve `getDataSlice()` remotely from
`node.js` or re-implement the JSON response protocol in any language. Just
return a `Promise()` from, or use an `async` function as an argument to,
`setDataListener()`. Your `` won't render until the `Promise` is
resolved, nor will it call your data model function again until the current call
is resolved or rejected. The following `async` example uses a Web Worker, but
the same principle applies to Web Sockets, `readFile()` or any other
asynchronous source. Returning a `Promise` blocks rendering until the Web Worker
replies:```javascript
// Browserlet callback;
worker.addEventListener("message", (event) => {
callback(event.data);
});regularTable.setDataListener((...viewport) => {
return new Promise(function (resolve) {
callback = resolve;
worker.postMessage(viewport);
});
});
``````javascript
// Web Workerself.addEventListener("message", async (event) => {
const response = await getDataSlice.apply(null, event.data);
self.postMessage(response);
});
```## `.addStyleListener()` and `getMeta()` Styling
`regular-table` can be styled trivially with just regular CSS for ``.
```css
// Zebra striping!
regular-table tr:nth-child(even) td {
background: rgba(0, 0, 0, 0.2);
}
```However, CSS alone cannot select on properties of your _data_ - if you scroll
this example, the 2nd row will always be the striped one. Some other
data-reliant style examples include:- Styling a specific column in the virtual data set, as `` may represent a
different column based on horizontal scroll position.
- Styling cells by value, +/-, heatmaps, categories, etc.
- Styling cells based on data within-or-outside of the virtual viewport,
grouping depth, grouping categories, etc.To make CSS that is virtual-data-model-aware, you'll need to use
`addStyleListener()`, which invokes a callback whenever the `` is
re-rendered, such as through API invocations of `draw()` and user-initiated
events such as scrolling. Within this optionally `async` callback, you can
select ``, ``, etc. elements via regular DOM API methods like
`querySelectorAll()`.```javascript
// Only select row_headers!
table.addStyleListener(() => {
for (const th of table.querySelectorAll("tbody th")) {
style_th(th);
}
});
```Once you've selected the `` and `` you want to paint, `getMeta()` will
return a `MetaData` record of information about the HTMLElement's virtual
position. This example uses `meta.x`, the position in `data`-space, to make
virtual-scroll-aware zebra striping.```javascript
function style_th(th) {
const meta = table.getMeta(th);
th.classList.toggle("zebra-striped", meta.x % 2 === 0);
}
``````css
.zebra-striped {
background-color: rgba(0, 0, 0, 0.2);
}
```### `.invalidate()`
To prevent DOM renders, `` conserves DOM calls like `offsetWidth`
to an internal cache. When a `` or ``'s `width` is modified within a
callback to `.addStyleListener()`, you must indicate to `` that
its dimensions have changed in order to invalidate this cache, or you may not
end up with enough rendered columns to fill the screen!A call to `invalidate()` that does not need new columns only imparts a small
runtime overhead to re-calculate virtual width per async draw iteration, but
should be used conservatively if possible. Calling `invalidate()` outside of a
callback to `.addStyleListener()` will throw an `Error`.```javascript
table.addStyleListener(() => {
for (const th of table.querySelectorAll("tbody th")) {
th.style.maxWidth = "20px";
}
table.invalidate();
});
```## `.addEventListener()` Interaction
`` is a normal `HTMLElement`! Use the `regular-table` API in
concert with regular DOM API methods that work on other `HTMLElement` to create
advanced functionality, such as this example of virtual row select:```javascript
const selected_rows = [];table.addEventListener("mousedown", (event) => {
const meta = table.getMeta(event.target);
if (meta && meta.y >= 0) {
selected_rows.push(meta.y);
table.draw();
}
});table.addStyleListener(() => {
for (const td of table.querySelectorAll("td")) {
const meta = table.getMeta(td);
td.classList.toggle("row-selected", selected_rows.includes(meta.y));
}
});
```Advanced examples can be found in the
[`examples`](https://github.com/finos/regular-table/tree/master/examples)
directory, and in the
[`bl.ocks` example gallery](https://github.com/finos/regular-table#examples).## Scrolling
Because of the structure of the HTML `` element, `` elements must be
aligned with their respective row/column, which causes default ``
to only be able to scroll in increments of a cell, which can be irregular when
column data is of different lengths. Optionally, you may implement _sub-cell
scrolling_ in CSS via `` slotted CSS variables. The provided
`material.css` theme does exactly this, or you can implement this in any custom
style by importing the `sub_cell_scrollling.css` stylesheet explicitly:```html
```
## Pivots, Filters, Sorts, and Column Expressions with `perspective`
`regular-table` is natively compatible with
[`perspective`](https://github.com/finos/perspective/), a WebAssembly streaming
visualization engine. By using a `perspective.Table` as a Virtual Data Nodel, it
becomes simple to achieve user-driven row and column pivots, filters, sorts, and
column expressions, as well as charts and persistent layouts, from
high-frequency updating data.## Development
First install `dev_dependencies`:
```bash
yarn
```Build the library
```bash
yarn build
```Run the test suite
```bash
yarn test
```Start the example server at
[`http://localhost:8080/examples/`](http://localhost:8080/examples/)```bash
yarn start
```#### OpenSSF
The Regular Table project achieves the
["Passing" Open Source Security Foundation (OpenSSF) Best Practices status](https://bestpractices.coreinfrastructure.org/en/projects/6771).## License
This software is licensed under the Apache 2.0 license. See the
[LICENSE](LICENSE) and [AUTHORS](AUTHORS) files for details.