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

defuss-dataview

v0.3.0

Published

Isomorphic functional data view (filters, sorters, paging, meta UI state) for table and tree grids.

Readme

defuss-dataview

Isomorphic functional data view (filters, sorters, paging, meta UI state) for table and tree grids.

defuss-dataview provides a tiny procedural API to define and apply an ADSD (Abstract Data State Description) for filtering, sorting, paging, and UI state.

  • JSON-first descriptor (filters, sorters, page, pageSize, meta, optional tree)
  • Sorters accept direction and alias dir
  • Zero-based paging (page: 0 is the first page)
  • Dot-path field access (for example user.profile.city)
  • Single apply contract: always returns [{ row, meta }]
npm install defuss-dataview
import { createDataview, applyDataview } from "defuss-dataview";

const view = createDataview({
  filters: [
    { field: "a", op: "eq", value: "Foo" },
    { field: "b", op: "eq", value: "Bar" },
  ],
  sorters: [
    { field: "a", direction: "desc" },
    { field: "id", direction: "asc" },
  ],
  page: 0,
  pageSize: 20,
});

const rows = [
  { id: 2, a: "Foo", b: "Bar" },
  { id: 1, a: "Foo", b: "Bar" },
  { id: 3, a: "Foo", b: "Baz" },
];

const entries = applyDataview(rows, view);

// [{ row, meta }]
// entries[0].row
// entries[0].meta

applyDataview never mutates the input array, so repeated apply calls on the same backing data are safe.

  • eq
  • neq
  • gt
  • gte
  • lt
  • lte
  • in
  • contains
  • startsWith
  • endsWith

Persist UI interactions (single/multi selection, locked columns, expand/collapse) in view.meta:

These helpers work for both flat tables and tree views.

import {
  createDataview,
  setSelectedRows,
  toggleSelectedRow,
  setLockedColumns,
} from "defuss-dataview";

let view = createDataview({ sorters: [{ field: "id", direction: "asc" }] });

view = setSelectedRows(view, [1, 2, 3]);
view = toggleSelectedRow(view, 2);
view = setLockedColumns(view, ["id", "name"]);

Use updateMeta when you want to update UI-only interaction state without touching filters/sorters/paging:

import { updateMeta } from "defuss-dataview";

view = updateMeta(view, {
  selectedRowIds: [1, 2, 3],
  lockedColumns: ["id", "name"],
});

updateMeta is ideal for:

  • row selection (selectedRowIds)
  • locked/frozen columns (lockedColumns)
  • combining multiple UI-meta updates in one state transition

For query-state changes like filtering, sorting, or pagination, use createDataview (not updateMeta).

Treat view as a controlled UI state object and re-apply on every interaction.

import {
  applyDataview,
  createDataview,
  updateMeta,
  toggleExpanded,
  toggleSelectedRow,
} from "defuss-dataview";

let view = createDataview({
  page: 0,
  pageSize: 25,
  sorters: [{ field: "id", dir: "asc" }],
  tree: {
    idField: "id",
    parentIdField: "parentId",
    expandedIds: [],
  },
});

const updateView = (next: typeof view) => {
  view = next;
  const entries = applyDataview(rows, view);
  render(entries); // your defuss/React/Vue/Svelte/DOM renderer
};

// select one row
const onRowClick = (id: number) => updateView(toggleSelectedRow(view, id));

// select all visible rows
const onSelectAllVisible = () => {
  const entries = applyDataview(rows, view);
  const ids = entries.map((entry) => entry.row.id as number);
  updateView(updateMeta(view, { selectedRowIds: ids }));
};

// collapse/expand one node
const onToggleNode = (id: number) => updateView(toggleExpanded(view, id));

// sort ("reorder")
const onSortChange = (field: string, direction: "asc" | "desc") =>
  updateView(
    createDataview({
      ...view,
      sorters: [{ field, direction }],
      page: 0,
    }),
  );

// filtering
const onFilterChange = (search: string) =>
  updateView(
    createDataview({
      ...view,
      filters: search
        ? [{ field: "title", op: "contains", value: search }]
        : [],
      page: 0,
    }),
  );

// pagination
const onPageChange = (page: number) =>
  updateView(
    createDataview({
      ...view,
      page,
    }),
  );

Rule of thumb:

  • updateMeta / selection / expansion helpers: interactive UI state
  • createDataview: structural query state (filters, sorters, page, pageSize, tree)

defuss-dataview now includes immutable data update helpers for UI-driven row changes:

  • updateRows(rows, ids, updates, idField?)
  • addRows(rows, newRows, anchorId?, position?, idField?)
  • removeRows(rows, ids, idField?)
  • setParent(rows, nodeId, parentId, idField?, parentIdField?)

After patching data, call applyDataview(data, view) again to re-compute visible entries and row meta.

import {
  applyDataview,
  addRows,
  setParent,
  updateRows,
  removeRows,
} from "defuss-dataview";

// table update (batch)
data = updateRows(
  data,
  [1, 3],
  [{ score: 99 }, { title: "Updated title" }],
);

// table insert: add row after id=3 (default position is "after")
data = addRows(data, [{ id: 8, title: "Inserted" }], 3);

// table remove
data = removeRows(data, [5, 9]);

// tree move: move node 8 under parent 2
data = setParent(data, 8, 2);

const entries = applyDataview(data, view);
render(entries);

Tree example (remove one node and its known subtree ids):

import { applyDataview, removeRows } from "defuss-dataview";

// remove node 7 and descendants 11 + 12
data = removeRows(data, [7, 11, 12]);

const entries = applyDataview(data, view);
render(entries);

Tree example (update one node in place):

import { applyDataview, updateRows } from "defuss-dataview";

data = updateRows(data, [7], [{ title: "Renamed node" }]);

const entries = applyDataview(data, view);
render(entries);

Tree behavior is enabled by adding tree options to createDataview. applyDataview still remains the only apply function.

import {
  createDataview,
  applyDataview,
  toggleExpanded,
  setSelectedRows,
} from "defuss-dataview";

let view = createDataview({
  tree: {
    idField: "id",
    parentIdField: "parentId",
    expandedIds: [1],
    includeAncestors: true,
    includeDescendantsOfMatch: false,
  },
  sorters: [{ field: "id", direction: "asc" }],
});

view = toggleExpanded(view, 2);
view = setSelectedRows(view, [4, 7]);

const entries = applyDataview(data, view);
// entries: [{ row, meta: { depth, hasChildren, isExpanded, isMatch, parentId, isSelected } }]

Core

  • createDataview(request)
  • applyDataview(data, view)
  • updateRows(rows, ids, updates, idField?)
  • addRows(rows, newRows, anchorId?, position?, idField?)
  • removeRows(rows, ids, idField?)
  • setParent(rows, nodeId, parentId, idField?, parentIdField?)

Grid & Tree Meta Helpers

  • updateMeta(view, updates)
  • setSelectedRows(view, ids)
  • toggleSelectedRow(view, id)
  • setLockedColumns(view, columns)

Tree-only Helpers

  • setExpandedIds(view, ids)
  • toggleExpanded(view, id)

Run a lightweight benchmark for 10k / 50k / 1M rows and advanced usage patterns (interaction loops, data update/add/remove flows, and tree apply scenarios):

pnpm --filter defuss-dataview benchmark