tree

Tree with custom indent.

Tree with custom indent.

Загрузка превью…

src/tree/c-tree-3.vue

<!--
  IconPlaceholder (dev-обвязка сайта ReUI) заменена инлайновым `<svg>`
  (lucide "folder-open"/"folder"/"file"), тем же приёмом, что и в
  apps/proof/src/cases/tree/vue.vue.
-->
<script setup lang="ts">
import { hotkeysCoreFeature, syncDataLoaderFeature } from "@headless-tree/core"
import { Tree, TreeItem, TreeItemLabel, useTree } from "@/components/reui/tree"

interface Item {
  name: string
  children?: string[]
}

const items: Record<string, Item> = {
  crm: {
    name: "CRM",
    children: ["leads", "accounts", "activities", "support"],
  },
  leads: {
    name: "Leads",
    children: ["new-lead", "contacted-lead", "qualified-lead"],
  },
  "new-lead": { name: "New Lead" },
  "contacted-lead": { name: "Contacted Lead" },
  "qualified-lead": { name: "Qualified Lead" },
  accounts: {
    name: "Accounts",
    children: ["acme-corp", "globex-inc"],
  },
  "acme-corp": {
    name: "Acme Corp",
    children: ["acme-contacts", "acme-opportunities"],
  },
  "acme-contacts": {
    name: "Contacts",
    children: ["john-smith", "jane-doe"],
  },
  "john-smith": { name: "John Smith" },
  "jane-doe": { name: "Jane Doe" },
  "acme-opportunities": {
    name: "Opportunities",
    children: ["website-redesign", "annual-maintenance"],
  },
  "website-redesign": { name: "Website Redesign" },
  "annual-maintenance": { name: "Annual Maintenance" },
  "globex-inc": {
    name: "Globex Inc",
    children: ["globex-contacts", "globex-opportunities"],
  },
  "globex-contacts": {
    name: "Contacts",
    children: ["alice-johnson"],
  },
  "alice-johnson": { name: "Alice Johnson" },
  "globex-opportunities": {
    name: "Opportunities",
    children: ["cloud-migration"],
  },
  "cloud-migration": { name: "Cloud Migration" },
  activities: {
    name: "Activities",
    children: ["calls", "meetings", "emails"],
  },
  calls: { name: "Calls" },
  meetings: { name: "Meetings" },
  emails: { name: "Emails" },
  support: {
    name: "Support",
    children: ["open-tickets", "closed-tickets"],
  },
  "open-tickets": { name: "Open Tickets" },
  "closed-tickets": { name: "Closed Tickets" },
}

const indent = 20

const tree = useTree<Item>({
  initialState: {
    expandedItems: ["leads", "accounts", "activities"],
  },
  indent,
  rootItemId: "crm",
  getItemName: (item) => item.getItemData().name,
  isItemFolder: (item) => (item.getItemData()?.children?.length ?? 0) > 0,
  dataLoader: {
    getItem: (itemId) => items[itemId]!,
    getChildren: (itemId) => items[itemId]!.children ?? [],
  },
  features: [syncDataLoaderFeature, hotkeysCoreFeature],
})
</script>

<template>
  <div class="mx-auto w-full grow place-self-start lg:w-xs">
    <Tree
      class="relative before:absolute before:inset-0 before:-ms-1 before:bg-[repeating-linear-gradient(to_right,transparent_0,transparent_calc(var(--tree-indent)-1px),var(--border)_calc(var(--tree-indent)-1px),var(--border)_calc(var(--tree-indent)))]"
      :indent="indent"
      :tree="tree"
    >
      <TreeItem v-for="item in tree.getItems()" :key="item.getId()" :item="item">
        <TreeItemLabel class="before:bg-background relative before:absolute before:inset-x-0 before:-inset-y-0.5 before:-z-10">
          <span class="flex items-center gap-2">
            <template v-if="item.isFolder()">
              <svg
                v-if="item.isExpanded()"
                xmlns="http://www.w3.org/2000/svg"
                width="24"
                height="24"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                aria-hidden="true"
                class="text-muted-foreground pointer-events-none size-4"
              ><path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" /></svg>
              <svg
                v-else
                xmlns="http://www.w3.org/2000/svg"
                width="24"
                height="24"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
                aria-hidden="true"
                class="text-muted-foreground pointer-events-none size-4"
              ><path d="M20 20a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2h-7.9a2 2 0 0 1-1.69-.9L9.6 3.9A2 2 0 0 0 7.93 3H4a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2Z" /></svg>
            </template>
            <svg
              v-else
              xmlns="http://www.w3.org/2000/svg"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
              stroke-linecap="round"
              stroke-linejoin="round"
              aria-hidden="true"
              class="text-muted-foreground pointer-events-none size-4"
            ><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" /><path d="M14 2v4a2 2 0 0 0 2 2h4" /></svg>
            {{ item.getItemName() }}
          </span>
        </TreeItemLabel>
      </TreeItem>
    </Tree>
  </div>
</template>

Установка

npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-tree-3.json

Зависимости реестра

npm-зависимости

  • @headless-tree/core

Источник: порт из ReUI (Keenthemes, MIT)