{
  "name": "TreeView",
  "source": "src/components/TreeView.tsx",
  "propsType": "TreeViewProps",
  "description": "TreeView — a hierarchy you open level by level: folders, an org chart,\nnested permissions.\n\nNOT FOR NAVIGATION. A site's own pages, nested, are Sidebar sections: links\nwith `aria-current`, reached by Tab. A tree is one tab stop that the arrow\nkeys move inside, which is right for data and wrong for a menu of pages.\n\nHOW IT IS BUILT\n\n  - React Aria's `useTree` and `useTreeItem`, over `useTreeState`: a\n    `treegrid` of flat rows, each with `aria-level`, `aria-expanded` and\n    its position among its siblings. One tab stop; ↑ ↓ move between visible\n    rows, → opens a row, ← closes it or moves to its parent, Home and End\n    go to the ends, and typing jumps to a label. A second → on an open row\n    stays put: in a treegrid → moves into the row's cells, not its children\n    — ↓ is the way down.\n  - The chevron is a button out of the tab order, named \"Expand\" or\n    \"Collapse\" in the user's locale, so a pointer can open a row that a\n    press would select.\n  - Only open rows are rendered. A closed branch's children are not in the\n    DOM, and the keyboard cannot land on them.\n  - `multiple` draws a check in each row — MultiSelect's box, for the same\n    reason: \"pick several\" has to look like it. Selection does not cascade:\n    a parent is an item of its own, and ticking it ticks only it.",
  "import": "import { TreeView } from 'ionbase-ui';",
  "status": "stable",
  "summary": "A hierarchy opened level by level — folders, an org chart, nested categories. One tab stop, arrow keys inside, only open rows rendered; selectable one or many.",
  "useWhen": [
    "the data is a hierarchy of unknown depth and the user needs to see where an item sits in it — a folder of knowledge files, teams within departments",
    "people pick one node, or several, from that hierarchy — a destination folder, the sources an agent may read",
    "the hierarchy is too large to show open, so levels open on demand"
  ],
  "useInstead": [
    {
      "when": "the hierarchy is the app's own pages — navigation",
      "use": "Sidebar",
      "why": "navigation is links reached by Tab, with `aria-current`. A tree is one tab stop with arrow keys inside, which hides every page from Tab and is announced as data, not as navigation"
    },
    {
      "when": "there are two levels and the second is short — sections of settings, FAQ groups",
      "use": "Accordion",
      "why": "a tree's keyboard model is overhead for a list of headings with content under each"
    },
    {
      "when": "the rows have columns — size, owner, modified",
      "use": "TreeGrid",
      "why": "TreeView is a single-column treegrid; nested rows with columns to compare down are a TreeGrid"
    },
    {
      "when": "the items are flat — records to open or pick, with no nesting",
      "use": "List",
      "why": "a tree's levels and expand keys are overhead when there is nothing to open"
    },
    {
      "when": "picking several values from a flat list",
      "use": "MultiSelect"
    }
  ],
  "a11y": {
    "requires": [
      "`aria-label` or `aria-labelledby` — the tree's name, such as \"Knowledge files\"",
      "a unique `id` on every item across the whole tree — keys are how selection and expansion are held",
      "a string `label`, or `textValue` when the label is not text — typeahead matches it and it is the row's name"
    ],
    "guarantees": [
      "a `treegrid` of flat rows, each with `aria-level`, `aria-posinset` and `aria-setsize` among its siblings, and `aria-expanded` only on rows that have children",
      "one tab stop: ↑ and ↓ move between visible rows, → opens a row, ← closes it or moves to its parent, Home and End go to the ends, and typing jumps to a matching label",
      "only open rows are rendered, so a closed branch cannot be landed on",
      "each parent's chevron is a button named \"Expand\" or \"Collapse\" in the user's locale, out of the tab order, and opens the row without selecting it",
      "`selectionMode=\"multiple\"` sets `aria-multiselectable` and draws a check in every row; the row's `aria-selected` is the announcement",
      "a disabled item is skipped by the arrow keys and cannot be selected",
      "`description` is read as the row's description",
      "every level indents by spacing/20, from the right in a right-to-left locale, where ← opens",
      "rows are at least 32px tall with a 24px chevron target; the keyboard focus ring is drawn on the row"
    ],
    "notes": [
      "→ on a row that is already open stays on it: in a treegrid → moves into a row's own cells, not its children. ↓ goes on to the first child",
      "selection does not cascade: selecting a parent selects that item only. A permissions tree where a parent means \"all of these\" needs the caller to expand the selection",
      "`onAction` fires on Enter, and on a press when nothing is selectable — for \"open this\": show the file, go to the folder. With `selectionMode=\"none\"` and no `onAction`, a press on a parent opens or closes it"
    ]
  },
  "antiPatterns": [
    {
      "dont": "<TreeView items={pages} onAction={navigate} /> for the app's own sections",
      "do": "<Sidebar> with a SidebarSection per group",
      "why": "pages in a tree are hidden from Tab and read as data; Sidebar's rows are links"
    },
    {
      "dont": "a tree of nested Accordions or indented Checkboxes",
      "do": "<TreeView selectionMode=\"multiple\" items={…} />",
      "why": "nested accordions are a tab stop per heading, and indented checkboxes carry no level — a screen reader hears a flat list"
    },
    {
      "dont": "ids that repeat under different parents — two \"docs\" folders with id \"docs\"",
      "do": "ids from the path or the record — \"policies/docs\", \"fld_812\"",
      "why": "expansion and selection are held by id, so two rows with one id open, close and select together"
    },
    {
      "dont": "rendering every level open to show the whole hierarchy",
      "do": "`defaultExpandedKeys` for the path to what matters, the rest closed",
      "why": "a tree opened in full is a long flat list with indentation, and loses the one thing a tree gives: seeing the shape before the detail"
    }
  ],
  "stylesheet": "src/styles/tree-view.css",
  "tokens": [
    "--border-disabled",
    "--border-focus",
    "--border-primary-strong",
    "--border-stronger",
    "--border-width-default",
    "--border-width-thick",
    "--font-family-sans",
    "--font-weight-medium",
    "--icon-default",
    "--icon-disabled",
    "--icon-on-color",
    "--icon-primary",
    "--icon-secondary",
    "--icon-size-sm",
    "--ion-duration-base",
    "--ion-ease-out",
    "--ion-shadow-raised-flush-xs",
    "--ion-tree-view-indent",
    "--ion-tree-view-level",
    "--radius-md",
    "--radius-sm",
    "--radius-xs",
    "--spacing-16",
    "--spacing-2",
    "--spacing-20",
    "--spacing-24",
    "--spacing-32",
    "--spacing-4",
    "--spacing-8",
    "--surface-default",
    "--surface-disabled",
    "--surface-hover",
    "--surface-primary",
    "--surface-selected",
    "--surface-selected-hover",
    "--text-default",
    "--text-disabled",
    "--text-secondary",
    "--text-tertiary",
    "--type-body-sm",
    "--type-body-sm-line-height",
    "--type-caption",
    "--type-caption-line-height"
  ],
  "props": {
    "items": {
      "type": "readonly TreeViewItem[]",
      "required": true,
      "origin": "own"
    },
    "aria-label": {
      "type": "string | undefined",
      "required": false,
      "origin": "own",
      "description": "Names the tree. Required unless `aria-labelledby` is given."
    },
    "aria-labelledby": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    },
    "expandedKeys": {
      "type": "Iterable<string> | undefined",
      "required": false,
      "origin": "own",
      "description": "Rows that are open. Controlled."
    },
    "defaultExpandedKeys": {
      "type": "Iterable<string> | undefined",
      "required": false,
      "origin": "own",
      "description": "Rows open at first."
    },
    "onExpandedChange": {
      "type": "((keys: Set<string>) => void) | undefined",
      "required": false,
      "origin": "own"
    },
    "selectionMode": {
      "type": "TreeViewSelectionMode | undefined",
      "required": false,
      "origin": "own",
      "description": "`none` (default): a row press opens or closes it, or fires `onAction`.\n`single` / `multiple`: a press selects, and `multiple` draws a check.",
      "values": [
        "multiple",
        "single",
        "none"
      ]
    },
    "selectedKeys": {
      "type": "Iterable<string> | undefined",
      "required": false,
      "origin": "own"
    },
    "defaultSelectedKeys": {
      "type": "Iterable<string> | undefined",
      "required": false,
      "origin": "own"
    },
    "onSelectionChange": {
      "type": "((keys: Set<string>) => void) | undefined",
      "required": false,
      "origin": "own"
    },
    "onAction": {
      "type": "((key: string) => void) | undefined",
      "required": false,
      "origin": "own",
      "description": "A row was activated — Enter, or a press when nothing is selectable. For\n\"open this\": navigating to a folder, showing a record."
    },
    "id": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    },
    "className": {
      "type": "string | undefined",
      "required": false,
      "origin": "own"
    }
  },
  "propCounts": {
    "own": 13,
    "aria": 0,
    "dom": 0,
    "other": 0
  }
}
