defuss-dataview
v0.3.0
Published
Isomorphic functional data view (filters, sorters, paging, meta UI state) for table and tree grids.
Maintainers
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, optionaltree) - Sorters accept
directionand aliasdir - Zero-based paging (
page: 0is the first page) - Dot-path field access (for example
user.profile.city) - Single apply contract: always returns
[{ row, meta }]
npm install defuss-dataviewimport { 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].metaapplyDataview never mutates the input array, so repeated apply calls on the same backing data are safe.
eqneqgtgteltlteincontainsstartsWithendsWith
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 statecreateDataview: 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