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

@lemonadejs/treeview

v6.0.0-beta.0

Published

LemonadeJS treeview block — contract-verified, framework-agnostic.

Readme

<Treeview /> — @lemonadejs/treeview

LemonadeJS treeview block — contract-verified, framework-agnostic.

✓ verified — 10 contract checks · framework-agnostic · zero dependencies

Overview

— LemonadeJS v6 block.

A hierarchical list rendered by ONE recursive view function: nodeView calls itself for node.children, every repeated is keyed by the node id, and the whole tree hangs off a single live expression reading the data state. Mutate the tree in place + data.touch() (or assign a new array) and the keyed diff moves/keeps existing DOM instead of rebuilding it — including NESTED sibling lists, which the engine compares structurally.

Collapse keeps the child DOM ALIVE: visibility is the aria-expanded attribute + CSS (display: none on the group), never an unmount. Same choice as panels. Rationale: re-expanding is instant, child DOM identity (and anything the host stuffed into it) survives toggles, the accessibility attribute IS the rendering switch (one source of truth), and toggling never re-runs the keyed diff. For huge lazy trees an unmounting branch would be the alternative; for a block-sized tree keep-alive is the idiomatic v6 answer.

Contract: data TreeNode[]: { id, label, icon?, open?, children? } bind two-way selected node id (bind="${state}") draggable opt-in drag-and-drop reordering (default false) onchange (id, node) — fires on user/api selection (parent writes to the bound state never echo back) ontoggle (id, open) — fires on expand/collapse (user or api) onmove (id, parentId, index) — after a drag drops a node into a new position (parentId is null at the root) api { open(id), close(id), select(id), toggle(id) }

Drag-and-drop (draggable only): a pointer gesture (3px threshold, Escape cancels, a drag never fires the click selection) with ZERO LAYOUT SHIFT — a dense list must not reflow under the cursor. The origin row stays in the flow, dimmed; a small chip with the node's label rides the cursor; the landing slot is an absolutely positioned insertion line (before/after) or a ring on the container row (inside). Hovering a CONTAINER row (a node with a children array — children: [] is an empty folder) splits it in thirds: top → drop BEFORE (sibling), bottom → drop AFTER (sibling), middle → drop INSIDE (becomes a child, opening the target). A LEAF row (no children key) splits in halves, before/after only — a leaf never becomes a parent via drop. The tree array is mutated in place + data.touch(), so the keyed diff MOVES existing DOM instead of rebuilding it. A node can never drop onto itself or into its own subtree.

Keyboard (APG tree pattern, single select): ArrowRight opens a closed parent; on an open parent moves to the first child ArrowLeft closes an open parent; otherwise moves to the parent ArrowUp/Down move focus across VISIBLE nodes Enter selects the focused node

Install

npm install @lemonadejs/treeview
import Treeview from '@lemonadejs/treeview';
import '@lemonadejs/treeview/style.css';

Usage

import { html, mount } from 'lemonadejs';

const App = () => html`<div>
    <${Treeview} />
</div>`;

mount(App, document.getElementById('root'));

Three deployment forms, one component:

html`<${Treeview} />`                       // by value (no registration)
setComponents({ Treeview });               // then <Treeview /> by name anywhere
createWebComponent(Treeview);              // <lm-treeview> in plain HTML/any framework

Props

Every declared prop arrives as a live state — pass a value for a snapshot or a state for a two-way live wire. Attribute strings are coerced to the declared type.

| Prop | Type | Default | Description | |---|---|---|---| | bind | any | — | Two-way bound value. .set() fires onchange; plain assignment is silent. two-way selected node id — 'any': ids are string | number | | data | array | — | TreeNode[] — the tree | | draggable | boolean | false | opt-in drag-and-drop reordering |

Events

All event names are lowercase (the platform convention — LJS-305 warns otherwise).

  • onchange — (id, node) on selection
  • ontoggle — (id, open) on expand/collapse
  • onmove — (id, parentId, index) after a drag drop

API (via ref)

import { ref } from 'lemonadejs';
const treeview = ref();
html`<${Treeview} ref="${treeview}" />`;
// treeview.current.open(...)  ·  treeview.current.close(...)  ·  treeview.current.select(...)  ·  treeview.current.toggle(...)
  • open()
  • close()
  • select()
  • toggle()

Styling

All classes follow the lm-treeview-* convention; visual variants are data-* attributes on the root. Override freely — there is no styling engine to fight.

Contract

The machine-readable schema ships with the package:

import contract from '@lemonadejs/treeview/contract.json';

verify.json carries the conformance proof produced by verify(Treeview).