npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lnsy/data-table

v0.1.0

Published

A dependency-free <data-table> web component: spreadsheet, table and list modes with formulas, endpoint lookups, wikilinks and file I/O.

Readme

@lnsy/data-table

A dependency-free tabular data component (<data-table>) built on vanilla JS, CSS and HTML — no jspreadsheet, no grid library.

One custom element, three presentation modes, all backed by the same data model and formula engine.

Features

Three modes, one engine

Pick a presentation with the mode attribute. The data model, formulas and [[wikilinks]] behave identically in all three:

| Mode | Attribute | What you get | | --- | --- | --- | | Spreadsheet | mode="spreadsheet" (default) | Full grid with A/B/C column headers and numbered rows, a formula bar, range selection, in-cell editing, and resizable columns and rows. | | Table | mode="table" | Searchable, sortable record table. The first data row becomes the header; click a header to sort ascending/descending (numeric-aware); a live counter shows filtered vs. total records. | | List | mode="list" | Searchable single-column list built from the first column; empty cells are skipped. |

Spreadsheet editing

  • Click to select; drag or Shift+Arrow to extend a range selection; arrow keys and Tab/Shift+Tab move the active cell.
  • Double-click, press Enter, or just start typing to edit a cell. Enter commits and moves down, Tab/Shift+Tab commits and moves sideways, Escape cancels.
  • Delete/Backspace clears the selection.
  • Drag the edge of a column or row header to resize it; double-click the edge to snap back to the default size.
  • The formula bar shows the raw contents of the active cell (formulas included), and cells that contain formulas are visually flagged.
  • read-only disables all editing while keeping selection, navigation and copy.

Formulas

Cells starting with = are evaluated by the built-in, dependency-free formula engine (src/formula.js):

=SUM(A1:A5)                 =IF(A1>2, "yes", "no")
=A1&B1                      =ROUND(A1*B1, 2)
=IFERROR(HTTP(…), "n/a")    =CONCAT("Q", 1)
  • References — A1, absolute \$B\$2, and ranges A1:B10 (ranges are valid inside functions).
  • Operators — + - * / ^ % (modulo), & (string concat), and comparisons = <> < > <= >=, with parentheses and unary minus.
  • Literals — numbers, "strings" (escape " as ""), TRUE/FALSE.

| Category | Functions | | --- | --- | | Math | SUM, AVERAGE/AVG, MIN, MAX, COUNT, COUNTA, ABS, ROUND, FLOOR, CEILING/CEIL, SQRT, POWER/POW, MOD | | Text | CONCAT/CONCATENATE, LEN, UPPER, LOWER, TRIM | | Logic | IF, IFERROR, AND, OR, NOT | | Date | TODAY, NOW |

IF and IFERROR short-circuit: only the taken branch is evaluated, which matters when a branch hits the network. Circular references are detected and reported as #CIRCULAR!; other errors surface as #SYNTAX!, #NAME?, #HTTP! or #NOENDPOINT!.

Live data from formulas

Formulas can fetch over HTTP, with dotted-path extraction into the parsed JSON (numeric segments index arrays):

=HTTP("https://api.example.com/status")
=HTTP("https://api.frankfurter.app/latest?from=USD", "rates.EUR")
=HTTPJSON("https://…", "items.0.name")
=ENDPOINT("rates", "rates.EUR")
  • HTTP(url) returns the response body (text, or stringified JSON); pass a second argument to extract a value from the parsed JSON.
  • HTTPJSON(url, path) always parses the body as JSON.
  • ENDPOINT("name", path?) reads a URL registered from JavaScript:
table.setEndpoints({ rates: 'https://api.frankfurter.app/latest?from=USD' });

Responses are cached for 30 seconds and concurrent identical requests are deduplicated; failed requests are only cached for 3 seconds so retries happen quickly.

Wikilinks

[[Page Name]] and [[Page Name|label]] render as links inside any cell:

  • Set wikilink-base-url="https://…/" and links open ${base}${encodeURIComponent(target)} in a new tab.
  • Otherwise, clicking a link dispatches a bubbling wikilink-navigate event with { detail: { target } }, which the host application can handle (routing, filtering, opening a panel…).

Copy & paste

Ctrl/Cmd+C/X/V work inside the grid and interoperate with Excel, Numbers and Google Sheets via tab-separated values. Pasting a multi-cell block expands the grid to fit. Programmatic equivalents: selectionToTSV(), copySelection() and pasteText(text).

File I/O

The helpers in src/file-io.js load, save, import and export an element's data. Wire them to your own buttons or menus; they operate on any <data-table> instance:

| Function | Direction | Format | | --- | --- | --- | | saveLSS(table, filename) / loadLSS(table, file) | export / import | Native .lss (JSON; keeps formulas as =… strings; reads older v1/v2 files) | | exportCSV(table, filename) / importCSV(table, file) | export / import | CSV (export writes computed values, like spreadsheet apps) | | exportJSON(table, filename) / importJSON(table, file) | export / import | JSON two-dimensional array | | importSpreadsheetFile(table, file) | import | XLSX, XLS and ODS |

These helpers are the package's only code that uses an npm dependency (xlsx, for the spreadsheet import); the component itself has none.

Theming

Styles ship as @lnsy/data-table/data-table.css, scoped to the component (data-table, .dt-*, a.wikilink) so nothing leaks into your page. The look is driven by CSS custom properties defined in the package's styles/variables.css — a "dataroom" theme (warm paper, dark-red accents) with an automatic dark scheme via prefers-color-scheme. Import that file or define your own values for the properties (e.g. --selection-bg, --grid-cell-height) to re-theme the component.

Single sheet by design.

Install

npm install @lnsy/data-table

Usage

Import the module (this registers the <data-table> custom element) and the stylesheet:

import '@lnsy/data-table';
import '@lnsy/data-table/data-table.css';

Then use the element anywhere in your HTML:

<data-table id="grid" mode="spreadsheet" rows="20" cols="8"></data-table>

The class is also exported for programmatic use:

import DataTable from '@lnsy/data-table';

Attributes

| Attribute | Values | Default | | ------------------- | ---------------------------------- | -------------- | | mode | spreadsheet | table | list | spreadsheet | | read-only | boolean | off | | rows / cols | integers (spreadsheet mode) | 50 / 12 | | wikilink-base-url | URL prefix for [[wikilinks]] | — |

Events

| Event | Fired when | detail | | ------------------- | ----------------------------- | ------------------------ | | change | any cell is edited | { row, col, value } | | selection-change | the active cell moves | { row, col } | | wikilink-navigate | a [[wikilink]] is clicked | { target } |

Element API

const table = document.querySelector('data-table');

table.setData([['name', 'qty'], ['nails', '100']]); // replace the contents
table.getData();              // deep copy of the raw grid
table.getComputedValue(1, 1); // formula-evaluated cell value
table.setCellValue(1, 1, '=A1*2');
table.setEndpoints({ rates: 'https://api.frankfurter.app/latest?from=USD' });
await table.recalculate();    // re-run every formula and re-render
table.insertRow();            // append a row (insertRow(5) appends five)
table.insertColumn();         // append a column
table.clearSelection();       // blank every cell in the selection
table.selectionToTSV();       // TSV of the current selection
table.pasteText('a\tb\nc\td'); // write a TSV block at the active cell

Development

This repository is the package's development home; the npm package itself only exports <data-table> (see src/index.js). The demo pages, command panel wiring and file menus live in the root index.js and are not part of the published package.

npm install        # install dependencies
npm start          # dev server on http://localhost:3000
npm run build      # production bundle in dist/
npm run test:unit  # vitest unit tests (tests/unit/)
npm test           # playwright end-to-end tests (tests/e2e/)

Demo pages: demo.html links to one demo per feature (endpoints, wikilinks, copy-paste, file I/O, table/list modes, live chart).

Publishing

npm version patch   # or minor / major
npm publish         # prepublishOnly builds and runs the unit tests

Verify the package contents with npm pack --dry-run: only src/, styles/, README.md and LICENSE ship.

License

Unlicense — public domain.