{"id":13725195,"url":"https://github.com/finos/regular-table","last_synced_at":"2025-10-24T13:43:00.483Z","repository":{"id":43402611,"uuid":"266174936","full_name":"finos/regular-table","owner":"finos","description":"A regular \u003ctable\u003e library, for async and virtual data models.","archived":false,"fork":false,"pushed_at":"2025-06-23T23:16:56.000Z","size":4518,"stargazers_count":372,"open_issues_count":16,"forks_count":39,"subscribers_count":17,"default_branch":"master","last_synced_at":"2025-07-17T08:08:34.963Z","etag":null,"topics":["data-visualization","javascript","jpmorganchase","table"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/finos.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":"AUTHORS","dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-22T17:57:59.000Z","updated_at":"2025-07-11T08:25:58.000Z","dependencies_parsed_at":"2024-05-01T22:13:52.798Z","dependency_job_id":"304002b7-f354-41ec-8a43-5f58cacfb21f","html_url":"https://github.com/finos/regular-table","commit_stats":null,"previous_names":[],"tags_count":39,"template":false,"template_full_name":null,"purl":"pkg:github/finos/regular-table","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finos%2Fregular-table","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finos%2Fregular-table/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finos%2Fregular-table/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finos%2Fregular-table/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/finos","download_url":"https://codeload.github.com/finos/regular-table/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/finos%2Fregular-table/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265872781,"owners_count":23842250,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-visualization","javascript","jpmorganchase","table"],"created_at":"2024-08-03T01:02:15.491Z","updated_at":"2025-10-24T13:43:00.406Z","avatar_url":"https://github.com/finos.png","language":"JavaScript","readme":"\u003cp align=\"center\"\u003e\n\u003cimg alt=\"regular-table\" src=\"https://raw.githubusercontent.com/finos/regular-table/master/logo.png\" width=\"300\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://community.finos.org/docs/governance/software-projects/stages/active/\"\u003e\u003cimg alt=\"FINOS active badge\" src=\"https://cdn.jsdelivr.net/gh/finos/contrib-toolbox@master/images/badge-active.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/regular-table\"\u003e\u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/v/regular-table.svg?color=brightgreen\u0026style=flat-squar\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/regular-table\"\u003e\u003cimg alt=\"NPM Version\" src=\"https://img.shields.io/npm/l/regular-table.svg?color=brightgreen\u0026style=flat-square\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/finos/regular-table/actions?query=workflow%3A%22Build+Status\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/finos/regular-table/workflows/Build%20Status/badge.svg?branch=master\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n#\n\nA Javascript library for the browser, `regular-table` exports a\n[custom element](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)\nnamed `\u003cregular-table\u003e`, which renders a regular HTML `\u003ctable\u003e` to a `sticky`\nposition within a scollable viewport. Only visible cells are rendered and\nqueried from a natively `async` virtual data model, making `regular-table` ideal\nfor enormous or remote data sets. Use it to build Data Grids, Spreadsheets,\nPivot Tables, File Trees, or anytime you need:\n\n-   Just a regular `\u003ctable\u003e`.\n-   Virtually rendered for high-performance.\n-   `async` data model handles slow, remote, enormous, and/or distributed\n    backends.\n-   Easy to style, works with any regular CSS for `\u003ctable\u003e`.\n-   Small bundle size, no dependencies.\n\n## Examples\n\n\u003ca href=\"https://finos.github.io/regular-table/block?example=two_billion_rows\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/two_billion_rows.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=canvas_data_model\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/canvas_data_model.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=minesweeper\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/minesweeper.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=file_browser\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/file_browser.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=spreadsheet\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/spreadsheet.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=row_mouse_selection\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/row_mouse_selection.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=area_mouse_selection\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/area_mouse_selection.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=row_stripes\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/row_stripes.png\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://finos.github.io/regular-table/block?example=column_mouse_selection\"\u003e\n\u003cimg width=\"30%\" src=\"https://finos.github.io/regular-table/img/column_mouse_selection.png\"/\u003e\n\u003c/a\u003e\n\u003cbr/\u003e\n\n-   [2d_array.md](examples/2d_array.md)\n-   [canvas_data_model.md](examples/canvas_data_model.md)\n-   [file_browser.md](examples/file_browser.md)\n-   [minesweeper.md](examples/minesweeper.md)\n-   [react.md](examples/react.md)\n-   [spreadsheet.md](examples/spreadsheet.md)\n-   [two_billion_rows.md](examples/two_billion_rows.md)\n\n## Documentation\n\nWhat follows functions as a quick-start guide, and will explain the basics of\nthe Virtual Data Models, Styling and Interaction APIs. Complete\n[API docs](https://github.com/finos/regular-table/blob/master/api.md) and\ndocumented\n[examples](https://github.com/finos/regular-table/tree/master/examples) are also\navailable.\n\n-   QuickStart\n\n    -   [Installation](#installation)\n    -   [`\u003cregular-table\u003e` Custom Element](#regular-table-custom-element)\n    -   [`.setDataListener()` Virtual Data Model](#setdatalistener-virtual-data-model)\n        -   [Column and Row Headers](#column-and-row-headers)\n        -   [Hierarchial/Group Headers](#hierarchialgroup-headers)\n        -   [`async` Data Models](#async-data-models)\n    -   [`.addStyleListener()` and `getMeta()` Styling](#addstylelistener-and-getmeta-styling)\n        -   [`.invalidate()`](#invalidate)\n    -   [`.addEventListener()` Interaction](#addeventlistener-interaction)\n    -   [Scrolling](#scrolling)\n    -   [Pivots, Filters, Sorts, and Column Expressions with `perspective`](#pivots-filters-sorts-and-column-expressions-with-perspective)\n    -   [Development](#development)\n\n-   [API Docs](https://github.com/finos/regular-table/blob/master/api.md)\n\n-   Annotated Examples\n    -   [2d_array.md](examples/2d_array.md)\n    -   [canvas_data_model.md](examples/canvas_data_model.md)\n    -   [file_browser.md](examples/file_browser.md)\n    -   [minesweeper.md](examples/minesweeper.md)\n    -   [react.md](examples/react.md)\n    -   [spreadsheet.md](examples/spreadsheet.md)\n    -   [two_billion_rows.md](examples/two_billion_rows.md)\n\n## Installation\n\nInclude via a CDN like [JSDelivr](https://cdn.jsdelivr.net/npm/regular-table):\n\n```html\n\u003cscript src=\"https://cdn.jsdelivr.net/npm/regular-table\"\u003e\u003c/script\u003e\n\u003clink\n    rel=\"stylesheet\"\n    href=\"https://cdn.jsdelivr.net/npm/regular-table/dist/css/material.css\"\n/\u003e\n```\n\nOr, add to your project via `npm`:\n\n```bash\nnpm add regular-table\n```\n\n... then import into your asset bundle.\n\n```javascript\nimport \"regular-table\";\nimport \"regular-table/dist/css/material.css\";\n```\n\n## `\u003cregular-table\u003e` Custom Element\n\n`regular-table` exports no symbols, only the `\u003cregular-table\u003e` Custom Element\nwhich is registered as a module import side-effect. Once loaded,\n`\u003cregular-table\u003e` can be used just like any other `HTMLElement`, using regular\nbrowser APIs:\n\n```javascript\nconst regularTable = document.createElement(\"regular-table\");\ndocument.body.appendChild(regularTable);\n```\n\n... or from regular HTML:\n\n```html\n\u003cregular-table\u003e\u003c/regular-table\u003e\n```\n\n... or from your library of choice, as long as it supports regular HTML! Here's\nan example for [React/JSX](https://reactjs.org/):\n\n```javascript\nconst App = () =\u003e \u003cregular-table\u003e\u003c/regular-table\u003e;\nReactDOM.render(\u003cApp /\u003e, document.getElementById(\"root\"));\n```\n\n## `.setDataListener()` Virtual Data Model\n\nLet's start with with a simple data model, a two dimensional `Array`. This one\nis very small at 3 columns x 6 rows, but even for very small data sets,\n`regular-table` won't read your entire dataset at once. Instead, we'll need to\nwrite a simple _virtual_ data model to access `DATA` and `COLUMN_NAMES`\nindirectly.\n\n```javascript\nconst DATA = [\n    [0, 1, 2, 3, 4, 5],\n    [\"A\", \"B\", \"C\", \"D\", \"E\", \"F\"],\n    [true, false, true, false, true, false],\n];\n```\n\nWhen clipped by the scrollable viewport, you may end up with a `\u003ctable\u003e` of just\na rectangular region of `DATA`, rather than the entire set. A simple viewport\n2x2 may yield this `\u003ctable\u003e`:\n\n\u003ctable\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e0\u003c/td\u003e\n\u003ctd\u003eA\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e1\u003c/td\u003e\n\u003ctd\u003eB\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n```json\n{\n    \"num_rows\": 26,\n    \"num_columns\": 3,\n    \"data\": [\n        [0, 1],\n        [\"A\", \"B\"]\n    ]\n}\n```\n\nHere's a an implementation for this simple _virtual_ data model, the function\n`getDataSlice()`. This function is called by your `\u003cregular-table\u003e` whenever it\nneeds more data, with coordinate arguments, `(x0, y0)` to `(x1, y1)`. Only this\nregion is needed to render the viewport, so `getDataSlice()` returns this\nrectangular `slice` of `DATA`. For the window (0, 0) to (2, 2), `getDataSlice()`\nwould generate an Object as above, containing the `data` slice, as well as the\noverall dimensions of `DATA` itself ( `num_rows`, `num_columns`), for sizing the\nscroll area. To render this virtual data model to a regular HTML `\u003ctable\u003e`,\nregister this data model via the `setDataListener()` method:\n\n```javascript\nfunction getDataSlice(x0, y0, x1, y1) {\n    return {\n        num_rows: (num_rows = DATA[0].length),\n        num_columns: DATA.length,\n        data: DATA.slice(x0, x1).map((col) =\u003e col.slice(y0, y1)),\n    };\n}\n\nregularTable.setDataListener(getDataSlice);\n```\n\nThis will render your regular HTML `\u003ctable\u003e` ! Your DOM will look something like\nthis, depending on the size of your viewport. Notice there are fewer rows and\ncolumns in the resulting HTML, e.g. the column `Column 3 (boolean)` - as you\nscroll, more data will be fetched from `getDataSlice()`, and parts of the\n`\u003ctable\u003e` will redrawn or extended as needed.\n\n```html\n\u003cregular-table\u003e\n    \u003ctable\u003e\n        \u003ctbody\u003e\n            \u003ctr\u003e\n                \u003ctd\u003e0\u003c/td\u003e\n                \u003ctd\u003eA\u003c/td\u003e\n            \u003c/tr\u003e\n            \u003ctr\u003e\n                \u003ctd\u003e1\u003c/td\u003e\n                \u003ctd\u003eB\u003c/td\u003e\n            \u003c/tr\u003e\n        \u003c/tbody\u003e\n    \u003c/table\u003e\n\u003c/regular-table\u003e\n```\n\n#### `virtual_mode` Option\n\n`regular-table` supports four modes of virtual scrolling, which can be\nconfigured via the `virtual_mode` optional argument. Note that using a\n`virtual_mode` other than the default `\"both\"` will render the _entire_\n`\u003ctable\u003e` along the non-virtual axis(es), and may cause rendering performance\ndegradation.\n\n-   \"both\" (default) virtualizes scrolling on both axes.\n-   \"vertical\" only virtualizes vertical (y) scrolling.\n-   \"horizontal\" only virtualizes horizontal (x) scrolling.\n-   \"none\" disable all scroll virtualization.\n\n```javascript\ntable.setDataListener(listener, { virtual_mode: \"vertical\" });\n```\n\n### Column and Row Headers\n\n`regular-table` can also generate Hierarchial Row and Column Headers, using\n`\u003cth\u003e` elements which layout in a `fixed` position within the virtual table. It\ncan generate Column Headers (within the `\u003cthead\u003e`), or Row Headers (the first\nchildren of each `tbody tr`), via the `column_headers` and `row_headers`\nproperties (respectively) of your data model's `Response` object. This can be\nrenderered with `column_headers`, a two dimensional `Array` which must be of\nlength `x1 - x0`, one `Array` for every column in your `data` window.\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eColumn 1 (number)\u003c/th\u003e\n\u003cth\u003eColumn 2 (string)\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003ctd\u003e0\u003c/td\u003e\n\u003ctd\u003eA\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e1\u003c/td\u003e\n\u003ctd\u003eB\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n```json\n{\n    \"num_rows\": 26,\n    \"num_columns\": 3,\n    \"data\": [\n        [0, 1],\n        [\"A\", \"B\"]\n    ],\n    \"column_headers\": [[\"Column 1 (number)\"], [\"Column 2 (string)\"]]\n}\n```\n\n### Hierarchial/Group Headers\n\n`regular-table` supports multiple `\u003ctr\u003e` of `\u003cth\u003e`, and also uses `colspan` and\n`rowspan` to merge simple consecutive names, which allows description of simple\nRow and Column Group Hierarchies such as this:\n\n\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth colspan=\"2\" rowspan=\"2\"\u003e\u003c/th\u003e\n\u003cth colspan=\"2\"\u003eColgroup 1\u003c/th\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003cth\u003eColumn 1\u003c/th\u003e\n\u003cth\u003eColumn 2\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\n\u003ctr\u003e\n\u003cth rowspan=\"2\"\u003eRowgroup 1\u003c/th\u003e\n\u003cth\u003eRow 1\u003c/th\u003e\n\u003ctd\u003e0\u003c/td\u003e\n\u003ctd\u003eA\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003cth\u003eRow 2\u003c/th\u003e\n\u003ctd\u003e1\u003c/td\u003e\n\u003ctd\u003eB\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\n\u003c/table\u003e\n\n```json\n{\n    \"num_rows\": 26,\n    \"num_columns\": 3,\n    \"data\": [\n        [0, 1],\n        [\"A\", \"B\"]\n    ],\n    \"row_headers\": [\n        [\"Rowgroup 1\", \"Row 1\"],\n        [\"Rowgroup 1\", \"Row 2\"]\n    ],\n    \"column_headers\": [\n        [\"Colgroup 1\", \"Column 1\"],\n        [\"Colgroup 1\", \"Column 2\"]\n    ]\n}\n```\n\nNote that in the rendered HTML, for these Row and Column `Array`, repeated\nelements in a sequence will be automatically merged via `rowspan` and `colspan`\nattributes. In this example, e.g. `\"Rowgroup 1\"` will only output to one `\u003cth\u003e`\nnode in the resulting `\u003ctable\u003e`.\n\nHeader merging can be disabled with the `merge_headers` option.\n\n### `metadata` Data-Aware Styling\n\nA `dataListener` may also optionally provide a `metadata` field in its response,\na two dimensional `Array` of the same dimensions as `data`. The values in this\nfield will accompany the metadata records returned by `regular-table`'s\n`getMeta()` method (as described in the next section).\n\n```json\n{\n    \"num_rows\": 26,\n    \"num_columns\": 3,\n    \"data\": [\n        [-1, 1],\n        [\"A\", \"B\"]\n    ],\n    \"metadata\": [\n        [\"pos\", \"neg\"],\n        [\"green\", \"red\"]\n    ]\n}\n```\n\n### Rendering Options\n\nAdditional rendering options which can be set on the object returned by a\n`setDataListener` callback include:\n\n-   `column_header_merge_depth: number` configures the number of rows to include\n    from `colspan` merging. This defaults to `header_length - 1`.\n-   `row_height: number` configures the pixel height of a row for virtual\n    scrolling calculation. This is typically auto-detected from the DOM, but can\n    be overridden if needed.\n-   `merge_headers: \"column\" | \"row\" | \"both\" | \"none\"` configures whether\n    equivalent, contiguous `\u003cth\u003e` elements are merged via `rowspan` or `colspan`\n    for `\"row\"` and `\"column\"` respectively (defaults to `\"both\"`).\n\n### `async` Data Models\n\nWith an `async` data model, it's easy to serve `getDataSlice()` remotely from\n`node.js` or re-implement the JSON response protocol in any language. Just\nreturn a `Promise()` from, or use an `async` function as an argument to,\n`setDataListener()`. Your `\u003cregular-table\u003e` won't render until the `Promise` is\nresolved, nor will it call your data model function again until the current call\nis resolved or rejected. The following `async` example uses a Web Worker, but\nthe same principle applies to Web Sockets, `readFile()` or any other\nasynchronous source. Returning a `Promise` blocks rendering until the Web Worker\nreplies:\n\n```javascript\n// Browser\n\nlet callback;\n\nworker.addEventListener(\"message\", (event) =\u003e {\n    callback(event.data);\n});\n\nregularTable.setDataListener((...viewport) =\u003e {\n    return new Promise(function (resolve) {\n        callback = resolve;\n        worker.postMessage(viewport);\n    });\n});\n```\n\n```javascript\n// Web Worker\n\nself.addEventListener(\"message\", async (event) =\u003e {\n    const response = await getDataSlice.apply(null, event.data);\n    self.postMessage(response);\n});\n```\n\n## `.addStyleListener()` and `getMeta()` Styling\n\n`regular-table` can be styled trivially with just regular CSS for `\u003ctable\u003e`.\n\n```css\n// Zebra striping!\nregular-table tr:nth-child(even) td {\n    background: rgba(0, 0, 0, 0.2);\n}\n```\n\nHowever, CSS alone cannot select on properties of your _data_ - if you scroll\nthis example, the 2nd row will always be the striped one. Some other\ndata-reliant style examples include:\n\n-   Styling a specific column in the virtual data set, as `\u003ctd\u003e` may represent a\n    different column based on horizontal scroll position.\n-   Styling cells by value, +/-, heatmaps, categories, etc.\n-   Styling cells based on data within-or-outside of the virtual viewport,\n    grouping depth, grouping categories, etc.\n\nTo make CSS that is virtual-data-model-aware, you'll need to use\n`addStyleListener()`, which invokes a callback whenever the `\u003ctable\u003e` is\nre-rendered, such as through API invocations of `draw()` and user-initiated\nevents such as scrolling. Within this optionally `async` callback, you can\nselect `\u003ctd\u003e`, `\u003cth\u003e`, etc. elements via regular DOM API methods like\n`querySelectorAll()`.\n\n```javascript\n// Only select row_headers!\ntable.addStyleListener(() =\u003e {\n    for (const th of table.querySelectorAll(\"tbody th\")) {\n        style_th(th);\n    }\n});\n```\n\nOnce you've selected the `\u003ctd\u003e` and `\u003cth\u003e` you want to paint, `getMeta()` will\nreturn a `MetaData` record of information about the HTMLElement's virtual\nposition. This example uses `meta.x`, the position in `data`-space, to make\nvirtual-scroll-aware zebra striping.\n\n```javascript\nfunction style_th(th) {\n    const meta = table.getMeta(th);\n    th.classList.toggle(\"zebra-striped\", meta.x % 2 === 0);\n}\n```\n\n```css\n.zebra-striped {\n    background-color: rgba(0, 0, 0, 0.2);\n}\n```\n\n### `.invalidate()`\n\nTo prevent DOM renders, `\u003cregular-table\u003e` conserves DOM calls like `offsetWidth`\nto an internal cache. When a `\u003ctd\u003e` or `\u003cth\u003e`'s `width` is modified within a\ncallback to `.addStyleListener()`, you must indicate to `\u003cregular-table\u003e` that\nits dimensions have changed in order to invalidate this cache, or you may not\nend up with enough rendered columns to fill the screen!\n\nA call to `invalidate()` that does not need new columns only imparts a small\nruntime overhead to re-calculate virtual width per async draw iteration, but\nshould be used conservatively if possible. Calling `invalidate()` outside of a\ncallback to `.addStyleListener()` will throw an `Error`.\n\n```javascript\ntable.addStyleListener(() =\u003e {\n    for (const th of table.querySelectorAll(\"tbody th\")) {\n        th.style.maxWidth = \"20px\";\n    }\n    table.invalidate();\n});\n```\n\n## `.addEventListener()` Interaction\n\n`\u003cregular-table\u003e` is a normal `HTMLElement`! Use the `regular-table` API in\nconcert with regular DOM API methods that work on other `HTMLElement` to create\nadvanced functionality, such as this example of virtual row select:\n\n```javascript\nconst selected_rows = [];\n\ntable.addEventListener(\"mousedown\", (event) =\u003e {\n    const meta = table.getMeta(event.target);\n    if (meta \u0026\u0026 meta.y \u003e= 0) {\n        selected_rows.push(meta.y);\n        table.draw();\n    }\n});\n\ntable.addStyleListener(() =\u003e {\n    for (const td of table.querySelectorAll(\"td\")) {\n        const meta = table.getMeta(td);\n        td.classList.toggle(\"row-selected\", selected_rows.includes(meta.y));\n    }\n});\n```\n\nAdvanced examples can be found in the\n[`examples`](https://github.com/finos/regular-table/tree/master/examples)\ndirectory, and in the\n[`bl.ocks` example gallery](https://github.com/finos/regular-table#examples).\n\n## Scrolling\n\nBecause of the structure of the HTML `\u003ctable\u003e` element, `\u003ctd\u003e` elements must be\naligned with their respective row/column, which causes default `\u003cregular-table\u003e`\nto only be able to scroll in increments of a cell, which can be irregular when\ncolumn data is of different lengths. Optionally, you may implement _sub-cell\nscrolling_ in CSS via `\u003cregular-table\u003e` slotted CSS variables. The provided\n`material.css` theme does exactly this, or you can implement this in any custom\nstyle by importing the `sub_cell_scrollling.css` stylesheet explicitly:\n\n```html\n\u003clink\n    rel=\"stylesheet\"\n    href=\"https://cdn.jsdelivr.net/npm/regular-table/dist/css/sub-cell-scrolling.css\"\n/\u003e\n```\n\n## Pivots, Filters, Sorts, and Column Expressions with `perspective`\n\n`regular-table` is natively compatible with\n[`perspective`](https://github.com/finos/perspective/), a WebAssembly streaming\nvisualization engine. By using a `perspective.Table` as a Virtual Data Nodel, it\nbecomes simple to achieve user-driven row and column pivots, filters, sorts, and\ncolumn expressions, as well as charts and persistent layouts, from\nhigh-frequency updating data.\n\n\u003c!-- add examples when perspective 0.5.1 is released --\u003e\n\n## Development\n\nFirst install `dev_dependencies`:\n\n```bash\npnpm install\n```\n\nBuild the library\n\n```bash\npnpm run build\n```\n\nRun the test suite\n\n```bash\npnpm run test\n```\n\nStart the example server at\n[`http://localhost:8080/examples/`](http://localhost:8080/examples/)\n\n```bash\npnpm run start\n```\n\n\u003c!--\n## Stats\n![npm bundle size](https://img.shields.io/bundlephobia/minzip/regular-table)\n--\u003e\n\n#### OpenSSF\n\nThe Regular Table project achieves the\n[\"Passing\" Open Source Security Foundation (OpenSSF) Best Practices status](https://bestpractices.coreinfrastructure.org/en/projects/6771).\n\n## License\n\nThis software is licensed under the Apache 2.0 license. See the\n[LICENSE](LICENSE) and [AUTHORS](AUTHORS) files for details.\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinos%2Fregular-table","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffinos%2Fregular-table","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffinos%2Fregular-table/lists"}