fantasia-he-tree

star 394

Hierarchical tree UI with @he-tree/vue only — full project replacement for Quasar QTree (forbidden). Virtualization, drag-and-drop, Quasar slot styling. Use when adding or changing any nested tree in the renderer.

vishiri By vishiri schedule Updated 6/10/2026

name: fantasia-he-tree description: >- Hierarchical tree UI with @he-tree/vue only — full project replacement for Quasar QTree (forbidden). Virtualization, drag-and-drop, Quasar slot styling. Use when adding or changing any nested tree in the renderer.

Fantasia Archive — hierarchical trees (@he-tree/vue)

Policy

Enforced detail: fa-he-tree.mdc.

  • @he-tree/vue only tree UI (package.json dependency).
  • Quasar QTree / q-tree forbidden — production, dialogs, layouts, Storybook, experiments.
  • Upstream: hetree.phphe.com (Vue 3 / v2).

Why he-tree (not QTree)

QTree excluded. @he-tree/vue: virtual list for scale, optional DnD, slots for Quasar-styled rows.

Installation (already in repo)

yarn add @he-tree/vue

Basic usage (Vue 3 + script setup)

<template>
  <Draggable
    v-model="treeData"
    virtualization
    class="myFeatureTree hasScrollbar"
    :style="{ height: treeHeightPx + 'px' }"
    data-test-locator="myFeature-tree"
  >
    <template #default="{ node, stat }">
      <!-- Quasar + i18n inside the slot -->
      <span
        class="myFeatureTree__label"
        :data-test-locator="'myFeature-tree-node-' + node.id"
      >
        {{ node.label }}
      </span>
    </template>
  </Draggable>
</template>

<script setup lang="ts">
import { Draggable } from '@he-tree/vue'
import '@he-tree/vue/style/default.css'

const treeData = defineModel<Array<{ id: string, label: string, children?: unknown[] }>>('treeData', {
  required: true
})

const treeHeightPx = 400
</script>
  • BaseTree — same API without drag when reorder not required.
  • Import @he-tree/vue/style/default.css in owning SFC or wrapper.

Virtualization checklist

  1. Prop virtualization on BaseTree / Draggable.
  2. Fixed height or max-height on tree or bounded scroll parent.
  3. Lazy-load children from main/SQLite on first expand for huge projects.
  4. Avoid expand-all on huge trees in one tick.

Related props: virtualization, virtualizationPrerenderCount.

Drag-and-drop

  • Draggable when users reorder nodes.
  • No vue-draggable-plus on trees — he-tree owns hierarchical reorder (fantasia-drag-drop).
  • Tune dragOverThrottleInterval on large trees.
  • Persist via Pinia + IPC after drop; validate in main with Zod where structured.

DnD + scroll preservation (layout tree playbook)

Full postmortem: .cursor/plans/he-tree-dnd-scroll-playbook_v2.4.14_2026-06-24-00-00-17.plan.md. Reference: DialogProjectSettingsWorldTemplateLayoutTree.vue.

Symptom: drop moves data OK; scrollTop jumps (often top). Or add row does not scroll into view.

Cause Fix
:key on Draggable changes on reorder/remount No :key for sort; resyncTreeDataFromProps updates treeData
Topology key uses draft array order or sort fields Canonical key: sorted ids + groupId only — mapDialogProjectSettingsWorldTemplateLayoutToTreeStructureKey
Resync rebuilds treeData when topology unchanged Match keys → patchWorldTemplateLayoutDisplayLabelsInHeTreeNodes only
overflow: auto on wrapper, not he-tree root Scroll on .dialogProjectSettingsWorldTemplateLayoutTree; host sizing only — resolveDialogProjectSettingsWorldTemplateLayoutTreeScrollContainer
Post-drop scrollTop restore Do not — fights virtualization; fix remount/rebuild instead

Pipeline: @before-drag-start → v-model during drag → @after-drop → deferred emitLayoutFromTreeDataIfChanged → props watch resyncTreeDataFromProps. Append: separate count watch → scheduleScrollContainerToRevealLastItem.

Debug: compare topology keys layout vs mapHeTreeNodesToWorldTemplateLayoutDraft(treeData) after drop; check resync rebuild vs patch; find real scroll element in DevTools.

Data and architecture

Concern Location
Node row UI, locators Feature .vue (thin script)
DB → nodes, filter, selection Feature scripts/ or src/scripts/<domain>/
Shared walk/flatten/id-index src/scripts/faHeTree/ when reused
Shared interfaces types/I_*.ts (app/types/...)

Two-level: pure transforms in functions/ (import type only); managers wire stores + IPC.

Styling

Utilities

import { walkTreeData } from '@he-tree/vue'

walkTreeData(nodes, (node, index, parent) => {
  // visit
}, { childrenKey: 'children' })

Use walkTreeData for search, bulk expand, validation — not ad hoc recursion everywhere.

Project Settings — world template layout

DialogProjectSettingsWorldTemplateLayoutTree.vueDraggable, max depth 2, DnD rules in dialogProjectSettingsWorldTemplateLayoutDnD.ts, commit policy + wiring in feature scripts/. DnD scroll playbook: he-tree-dnd-scroll-playbook plan. Full map: fa-he-tree.mdc and fa-drag-drop-lists.mdc.

Tests

  • Vitest — mount SFC; stub IPC; assert data-test-locator
  • Playwright — locators; rebuild Electron when wiring changes (fantasia-testing)
  • Storybook — modest mocked tree; import default CSS

Related docs

Install via CLI
npx skills add https://github.com/vishiri/fantasia-archive --skill fantasia-he-tree
Repository Details
star Stars 394
call_split Forks 57
navigation Branch main
article Path SKILL.md
More from Creator