vuetifyjs / vuetifyjs/0

[Feature Request] composable for searchable trees

Open
#469 1 comment 0 reactions 1 assignee View on GitHub

@johnleider is already working on this.

Since Jul 2, 2026.

Dominant language
TypeScript
Stars
735
Forks
10
Avg merge
10h 47m
Merged PRs (30d)
72

Description

Origin

High level: tree structures (treeview, datatables with group-by, lists) might benefit from headless composable driving search «» opened interaction while also supporting open-all.


Filtered reveal for nested trees (useNestedReveal + Treeview filter prop)

A searchable tree needs its matches to become visible — which means opening every collapsed ancestor of each match. Doing this naively (recompute openedIds from the match set on every change) breaks real UX in ways that are non-obvious until you hit them. This was discovered while patching Vuetify's VTreeview. PR attempts to introduce open.ts that should eventually be replaced by v0-native utility.

createNested already ships the mechanics — reveal(ids) (opens ancestors only), open/close, openedIds, getAncestors, isLeaf, and a registration-driven openAll. What is missing is the filter-reveal session policy: the rules for how open state should evolve as a query comes and goes, without fighting the user.

createFilter already produces the match set. Nothing wires the two together.

The policy (behavior spec)

Given a reactive set of matched ids (from createFilter) and a NestedContext:

  1. reveal, don't dump.
  • for each match, open nodes path (reveal)
  • open the matched node itself.
  1. additive, not authoritative.
  • merge reveal targets into openedIds. A branch the user collapses mid-search stays collapsed (until a new query makes it match again).
  1. remember only what the search opened.
  • track the ids this session actually opened (those not already open when revealed)
  1. on clear, undo only the temporarily revealed nodes — except what user opened deliberately mid-search
  2. accept live tree changes. — as matches appear/disappear (async load, edits), reveal nodes and leafs that match
Proposed API
export function useNestedReveal (
  nested: NestedContext,
  matchedIds: MaybeRefOrGetter<Iterable<ID>>,   // e.g. from createFilter → ids
  options?: { active?: MaybeRefOrGetter<boolean> }, // default: derive from matchedIds emptiness
): void
  • No return value; it drives nested.openedIds as a side effect while active.
  • active false (query cleared) triggers the rule-4 cleanup exactly once.
  • Pure policy over reveal/open/close/openedIds/getAncestors/getPath/
    isLeaf. Ships with createNested conceptually; keep it a separate export so
    non-tree consumers of createNested don't pay for it.
Component surface

Add a filter/query prop to Treeview.Root (mirrors openAll/reveal which already live there). Internally: createFilter → matched ids → useNestedReveal(nested, ids). Visibility of non-matches is a separate render concern (a matched(id) predicate on the
context for the item to consult), independent of open state — keep the two decoupled.

Test checklist (from Vuetify's VTreeview tests)
  • reveal a match nested under collapsed ancestors (both eager + lazy registration)
  • collapse a revealed ancestor mid-search → stays collapsed when search/query is cleared
  • clear removes only session-opened nodes
  • clear keeps a branch the user opened mid-search and its adopted ancestors
  • match added tree modes while active is revealed
  • openAll + collapsed nodes do not re-open accidentally

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.