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

@plotdb/sheet

v0.7.0

Published

spreadsheet

Readme

@plotdb/sheet

simple sheet UI in vanilla JS. Features:

  • fast, infinite scrolling.
  • small - < 25KB when minimized
  • vanilla - only optional depends on Papaparse for CSV parsing
  • CSS-based layout - better user experience
  • size of column/row auto fit content yet still customizable
  • support frozen row/column for data, fixed row/column for custom content
  • support tsv/csv pasting (depends on Papaparse)

Usage

install:

npm install @plotdb/sheet

include:

<script src="path-to/sheet/index.min.js"></script>
<link rel="stylesheet" type="text/css" href="path-to/sheet/index.min.css"/>
<div class="container"></div>

initialize:

var s = new sheet({
  root: '.container',
  data: mydata = [[... ], ... /* array of arrays */]
})

Constructor Options

  • root: container element or selector.
  • fixed: {row, col}. default {row: 0, col: 0}
  • frozen: {row, col}. default {row: 0, col: 0}
  • idx: {row, col}. default {row: true, col: true}
  • size: {row, col}. force cell size, in array by order.
    • default: {row: [], col: []}
    • example: {col: ["300px", "12em"]}
  • class: {row, col}. additional cell class name, in array by order, as space separated string.
    • default: {row: [], col: []}
  • editing: false to disable editing. default true.
    • (TBD) in the future we should be able to:
      • set edit permission ( e.g., edit, comment )
      • apply on range
  • scrollLock: default true. prevent from horizontal scrolling of document.body if true.
    • this affects how user interact with sheet with scrolling gestures. check src comment for more information.
    • see also enableScrolling
  • enableScrolling: default true. enable scrolling if true.
    • the difference between scrollLock and enableScrolling is that:
      • scrollLock is for horizontal scrolling that may conflict with browser gesture.
      • enableScrolling is for mouse / trackpad scrolling behavior
  • data: initial data, as array of arrays
  • slider: default false. show slider widgets if true.
  • cellcfg(opt): custom cell definition function. return value based on the given opt, which contains:
    • row: row of the cell to query
    • col: col of the cell to query
    • type: a string indicating what should we return. it can be:
      • class: should return the CSS classes for the given cell
      • readonly: return true if the given cell should be readonly.
      • format: return cell format for number. Use d3-format syntax.
        • depends on d3.format. if not available, no formatting will be applied.

API

  • set({row, col, data, range}): set cell data to data for cell(s) in row row and col col.
    • update a range based on 2D array data when range is true.
  • goto({row, col}): set grid view starting from coordinate {row, col}
  • render(): force to re-render visible cells
  • editing(v): set edit status to v, or return edit status if v is not provided.
  • data(d): replace data completely with d and re-render.
    • return current data if d is omitted.
    • d is in the same format with the constructor data option
  • size({row, col}): update grid size.
  • select(opt): set / get selection range
    • if opt is omitted, return current selection status, which is:
      • null, if no selection available.
      • the selection object with fields as below:
        • col: index of column. omitted for selecting everything on a row.
        • colspan: span of column. will be 1 if omitted
        • row: index of row. omitted for selecting everything on a column.
        • rowspan: span of row. will be 1 if omitted
      • {} = the whole table selected, based on the definition of the selection object.
    • otherwise, set selection based on the opt selection range object described as above.
  • slice(): delete selected rows/columns
  • insert(): insert empty row/column at selected location.
  • sort(opt): sort data based on selected column. opt is an object with following fields as options:
    • dir: direction. either asc or desc, by default asc.
  • edited(opt): force sheet enter non-edit mode. option:
    • cancel: default false. if true, cancel current editing and remove content that are going to be inputted.

Event

use sheet.on('event-name', callback) to handle sheet related events, e.g.,

s = new sheet( ... );
s.on('change', function({row, col, data, range}) {
  ....
});

Following are a list of possible events:

  • change: fired when some cells are updated. callback function with following options, in object:
    • row: base row of the updated cell
    • col: base col of the updated cell
    • data: data used to update. If it's a 2D array and range is true, then cells are updated with values in data correspondingly relative to row and col as the original cell
    • range: true if a range of cells are updated.
  • menu.on: fired when a contextmenu event is fired within sheet. Option is an object with following fields:
    • evt: corresponding event object
    • node: target node from which this event was fired.
  • menu.off: fired when a context menu is expected to dismiss. Option is an object with following fields:
    • evt: corresponding event object
    • node: target node from which this event was fired.

Rich Text

You can add more than texts in the grid of the sheet, by having an object with following structure in data:

{
  type: "data-type",
  ... /* members according to type */
}

Currently, only node type is supported:

{ type: "dom", node: SomeElement }

Swipe Back Issue

On Chrome in Mac, scrolling left with trackpad with two finger gesture may also triggers swipe back action, which navigate the page back in browsing history. This happens in all websites needing similar actions, including Airtable and Google Spreadsheet.

This may be temporarily solved by setting overscroll-behavior to contain of the sheet container, however - if swipe back happens once anywhere outside the sheet container, it then always triggered even in sheet container.

If this happens, try setting overscroll-behavior to contain directly on body to see if it may solve this issue.

Reference:

  • https://bugs.chromium.org/p/chromium/issues/detail?id=889846
  • https://bugs.chromium.org/p/chromium/issues/detail?id=862693
  • https://bugs.chromium.org/p/chromium/issues/detail?id=906886
  • https://community.airtable.com/t/3074

License

MIT