# rn-tree-select URL: /docs/native/docyrus/tree-select Trigger field that opens a searchable, flattened tree in a bottom sheet — single or multi select, leaf-only selection and breadcrumb display, API-aligned with the web TreeSelect. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-tree-select ``` **Dependencies:** - [@shopify/flash-list](https://www.npmjs.com/package/@shopify/flash-list) - [tailwind-variants](https://www.npmjs.com/package/tailwind-variants) ## Usage ```tsx import { useState } from 'react'; import { TreeSelect, type TreeSelectValue } from '@/components/docyrus-native/tree-select'; import { type TreeViewItem } from '@/components/docyrus-native/tree-view'; const data: TreeViewItem[] = [ { id: 'eng', name: 'Engineering', type: 'group', children: [ { id: 'eng-fe', name: 'Frontend', type: 'team' }, { id: 'eng-be', name: 'Backend', type: 'team' } ] } ]; export function TeamPicker() { const [team, setTeam] = useState(null); const [teams, setTeams] = useState([]); return ( <> ); } ``` ### Interaction model | Gesture | Single | Multiple | |---------|--------|----------| | Tap trigger | Opens the sheet (search + tree) | Opens the sheet | | Tap leaf row | Commits the id and closes | Toggles the leaf | | Tap folder row | Commits the folder id and closes; with `leafOnly` it expands / collapses instead | Toggles the folder — cascades to the folder id and every descendant, or only to leaf descendants with `leafOnly`; with `leafOnly` the row tap expands / collapses and the checkbox cascades | | Tap chevron | Expands / collapses | Expands / collapses | | Tap chip `×` | — | Removes the item (and its cascade) | | **Done** (header) | — | Closes the sheet | The sheet lists the tree as a **flattened `FlashList`** (depth indent, chevrons, tri-state checkboxes in multi mode — a folder shows *indeterminate* when only some of its leaves are selected), so large trees stay smooth. Searching filters by name and opens every folder on the path to a match. Expansion is re-seeded from `defaultExpanded` and the search is cleared every time the sheet opens (the web tree remounts per open). The multi-mode trigger shows up to `maxVisibleChips` chips and collapses the rest into `+N`. ## API Reference ### TreeSelectProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `data` | `TreeViewItem[]` | - | Tree data shown in the sheet | | `value` | `TreeSelectValue` | - | Selected id (single) or ids (multiple) | | `onValueChange` | `(value: TreeSelectValue) => void` | - | Selection changed — `string` in single mode, `string[]` in multi mode, `null` when cleared | | `multiple` | `boolean` | `false` | Multi-select mode (checkboxes + **Done** button) | | `placeholder` | `string` | `t('ui.treeSelect.placeholder', 'Select...')` / `t('ui.treeSelect.multiPlaceholder', 'Select items...')` | Trigger placeholder | | `disabled` | `boolean` | `false` | Disable the control | | `className` | `string` | - | Additional classes for the trigger | | `onBlur` | `() => void` | - | Called when the sheet closes (native stand-in for the trigger blur) | | `invalid` | `boolean` | `false` | Error border on the trigger (replaces web `aria-invalid`) | | `detent` | `'small' \| 'medium' \| 'large' \| 'fit'` | `'large'` | Sheet height (replaces web `maxHeight`) | | `getIcon` | `(item: TreeViewItem, depth: number) => ReactNode` | - | Custom icon renderer (wins over `iconMap`) | | `iconMap` | `TreeViewIconMap` | - | `item.type` → icon; a string renders as a `DocyrusIcon` identifier | | `hideInfo` | `boolean` | `true` | Hide the per-row info caption (`type · N direct items`) — the native counterpart of the web hover card | | `searchPlaceholder` | `string` | `t('ui.treeView.searchPlaceholder', 'Search...')` | Search placeholder | | `leafOnly` | `boolean` | `false` | Only leaves become values (see the interaction model) | | `defaultExpanded` | `'none' \| 'all' \| 'selected' \| ReadonlyArray` | `'selected'` | Folders expanded each time the sheet opens (`'selected'` = ancestors of the current value) | | `showBreadcrumb` | `boolean` | `false` | Render `Group › Subgroup › Item` in the trigger; ancestors are muted | | `renderSelected` | `(item: TreeViewItem) => ReactNode` | - | Custom trigger rendering for a selected item (wins over `showBreadcrumb`; strings are wrapped in `Text`) | | `open` | `boolean` | - | Controlled open state | | `onOpenChange` | `(open: boolean) => void` | - | Open state changed | | `sheetTitle` | `string` | placeholder | Sheet title (native addition) | | `maxVisibleChips` | `number` | `3` | Multi mode: chips shown before collapsing the rest into `+N` (native addition) | | `testID` | `string` | - | Test id for the trigger | | `accessibilityLabel` | `string` | placeholder | Accessibility label for the trigger | ### Differences from web - `ref`, `id`, `name` and `aria-invalid` are dropped — use `invalid`, `testID` and `accessibilityLabel`. - `maxHeight` is replaced by `detent`. - `sheetTitle` and `maxVisibleChips` are native additions. ## Translation keys Copy resolves through `useUiTranslation()` (explicit prop → `` → English fallback). Shared with web: `ui.treeSelect.placeholder`, `ui.treeSelect.multiPlaceholder`, `ui.treeSelect.removeItem`, `ui.treeView.searchPlaceholder`, `ui.treeView.noItemsFound`. Native additions: `ui.treeSelect.done`, `ui.treeView.selected`, `ui.treeView.expand`, `ui.treeView.collapse`, `ui.treeView.directItems`. ## Components | Component | Description | |-----------|-------------| | `TreeSelect` | Trigger + tree bottom sheet | ## Type Exports | Type | Description | |------|-------------| | `TreeSelectProps` | Props for `TreeSelect` | | `TreeSelectValue` | `string \| string[] \| null \| undefined` | | `TreeSelectDefaultExpanded` | `'none' \| 'all' \| 'selected' \| ReadonlyArray` |