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

@h-k-dev/angular-tree

v1.3.0

Published

High-performance headless Angular tree: virtualized (100k+ nodes), accessor-based, signals-only, zoneless, drag & drop with per-node rules. @angular/cdk is the only runtime dependency.

Readme

angular-tree

CI/CD npm license: MIT

High-performance, headless tree component for Angular. Zoneless, signals-only, virtualized from the first row — @angular/cdk is the only runtime dependency.

▶ Live demo — 100k-node mode, a drag & drop rules showcase, lazy loading with error + retry, context menus, inline rename, search, dark mode. The demo's full source lives in projects/app and doubles as the reference integration.

Why angular-tree

  • Virtualized, always — 100k+ nodes at 60fps (CDK virtual scroll, flat internal model, O(n) recompute). There is no non-virtualized mode: rows are fixed-height ([itemSize]) and their DOM is disposable by design — see Virtualization before building stateful row templates
  • Headless — the tree ships no UI it doesn't own: node content, checkboxes, editors, and menu items are your templates; the tree owns behavior, ARIA, and mechanics
  • Accessor-based — no forced node shape; describe your data with functions, never reshape it. Async accessors are lazy loading
  • Controlled — every mutation is an intent (moved, renamed, …) that you apply to your own state; the tree never touches your data
  • Complete interaction set — multi-select (Ctrl/Shift/checkbox cascade), drag & drop with per-node rules and copy-on-modifier, keyboard move (Ctrl+X/V), lazy loading with retry, inline rename, type-ahead, search filtering, RTL, built-in context-menu host
  • Accessible — full APG tree keyboard map, true aria-setsize/aria-posinset at virtualized edges, roving tabindex or active-descendant, screen-reader announcements — see Accessibility

Install

npm install @h-k-dev/angular-tree

Peer dependencies: @angular/core|common|cdk ≥ 21.2, rxjs ≥ 7.8.

Quick start

import { Component, signal } from '@angular/core';
import {
  AngularTree,
  MoveEvent,
  TreeNodeDef,
  TreeNodeToggle,
} from '@h-k-dev/angular-tree';

interface DocNode {
  id: string;
  name: string;
  children?: DocNode[];
}

@Component({
  selector: 'app-docs',
  imports: [AngularTree, TreeNodeDef, TreeNodeToggle],
  templateUrl: './docs.html',
})
export class Docs {
  roots = signal<DocNode[]>([/* your data */]);

  // Accessors DESCRIBE your data — the tree never mutates it.
  // Returning a Promise/Observable makes the node lazy.
  getChildren = (node: DocNode) => node.children;
  getKey = (node: DocNode) => node.id;
  isFolder = (node: DocNode) => node.children != null;

  // Mutations arrive as INTENTS — apply them to your own state, the tree re-renders.
  applyMove({ dragIds, parentId, index }: MoveEvent<DocNode>) {
    this.roots.update((roots) => moveNodes(roots, dragIds, parentId, index));
  }
}
<angular-tree
  #tree="angularTree"
  [dataSource]="roots()"
  [childrenAccessor]="getChildren"
  [expansionKey]="getKey"
  [itemSize]="32"
  (moved)="applyMove($event)"
>
  <!-- folder template — `when` predicates are typed type guards -->
  <ng-container
    *treeNodeDef="let node; when: isFolder; let isExpanded = isExpanded"
  >
    <button treeNodeToggle>{{ isExpanded ? '▾' : '▸' }}</button>
    <span>{{ node.name }}</span>
  </ng-container>

  <!-- leaf fallback -->
  <ng-template treeNodeDef let-node>{{ node.name }}</ng-template>
</angular-tree>

API at a glance

The most-used surface — the live demo includes a full API reference page.

| Input | Purpose | | ----------------------------------------------------------- | ------------------------------------------------------------------------- | | dataSource, childrenAccessor, expansionKey | Your data, described by functions; async children = lazy loading | | itemSize | Fixed row height in px — the virtualization contract | | selectedKeys, multi, checkboxSelection | Controlled selection — [(selectedKeys)] two-way over keys; optional cascade | | searchTerm, searchMatch | Filtering; matches keep their ancestor chain visible | | disableDrag, disableDrop, disableEdit, isSelectable | Per-node behavior predicates — type rules live in your code, not the tree | | rowClass, rowStyle | Per-node row styling accessors; rowStyle also reaches the group's guide | | childrenDeps | Declarative lazy-cache invalidation — bind what your accessor reads | | expandedKeys, defaultExpandedKeys | Controlled expansion — [(expandedKeys)] two-way; or initial-only keys | | loading, indentGuides | Root-level loading state, clickable guide lines |

| Output | Fires when | | --------------------------------------- | -------------------------------------------------------------------- | | activated | Row clicked / Enter — your "open" action | | moved | Drag or keyboard move — dragIds, parentId, index, dropEffect | | renamed, selectionChange, toggled | Inline rename commit, selection set change, expand/collapse | | childrenLoaded | Lazy load resolved or errored (pair with retryChildren) | | contextRequested | Right-click / Shift+F10 — feeds the built-in menu host |

Testing

@h-k-dev/angular-tree/testing ships a CDK test harness (TreeHarness, TreeNodeHarness) including a real drag-gesture simulation (dragTo).

Docs

  • Theming--tree-* tokens, Material system-token chain, row state hooks
  • Context menus — built-in host, external menu systems
  • Virtualization — sizing, autosize escape hatch
  • Accessibility — what the tree guarantees, the one row-template rule, announcements
  • Recipesmat-checkbox, loading masks, dialog refocus
  • Migration — from PrimeNG p-tree / jsTree: accessor adapters, CRUD → intents, synthetic nodes, typed actions

License

MIT