import {TreeView} from '@primer/react'
<Box sx={{maxWidth: 400}}><nav aria-label="Files"><TreeView aria-label="Files"><TreeView.Item id="src"><TreeView.LeadingVisual><TreeView.DirectoryIcon /></TreeView.LeadingVisual>src<TreeView.SubTree><TreeView.Item id="src/Avatar.tsx" onSelect={() => console.log('src/Avatar.tsx')}><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>Avatar.tsx<TreeView.TrailingVisual><StyledOcticon icon={DiffAddedIcon} color="success.fg" aria-label="added" /></TreeView.TrailingVisual></TreeView.Item><TreeView.Item id="src/Button.tsx" current><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>Button.tsx<TreeView.TrailingVisual><StyledOcticon icon={DiffModifiedIcon} color="attention.fg" aria-label="modified" /></TreeView.TrailingVisual></TreeView.Item></TreeView.SubTree></TreeView.Item><TreeView.Item id="package.json"><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>package.json<TreeView.TrailingVisual><StyledOcticon icon={DiffModifiedIcon} color="attention.fg" aria-label="modified" /></TreeView.TrailingVisual></TreeView.Item></TreeView></nav></Box>
<Box sx={{maxWidth: 400}}><nav aria-label="Files"><TreeView aria-label="Files"><TreeView.Item id="src"><TreeView.LeadingVisual><TreeView.DirectoryIcon /></TreeView.LeadingVisual>src<TreeView.SubTree><TreeView.Item id="src/Avatar.tsx"><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>Avatar.tsx</TreeView.Item><TreeView.Item id="src/Button" current><TreeView.LeadingVisual><TreeView.DirectoryIcon /></TreeView.LeadingVisual>Button<TreeView.SubTree><TreeView.Item id="src/Button/Button.tsx"><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>Button.tsx</TreeView.Item><TreeView.Item id="src/Button/Button.test.tsx"><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>Button.test.tsx</TreeView.Item></TreeView.SubTree></TreeView.Item></TreeView.SubTree></TreeView.Item><TreeView.Item id="package.json"><TreeView.LeadingVisual><FileIcon /></TreeView.LeadingVisual>package.json</TreeView.Item></TreeView></nav></Box>
import {Link, useMatch, useResolvedPath, navigate} from 'react-router-dom'import {TreeView} from '@primer/react/drafts'function TreeLinkItem({id, to, children}) {const resolved = useResolvedPath(to)const isCurrent = useMatch({path: resolved.pathname, end: true})return (<TreeList.Itemid={id}aria-current={isCurrent ? 'page' : undefined}current={isCurrent}onSelect={() => navigate(to)}>{children}</TreeList.Item>)}function App() {return (<nav aria-label="Files"><TreeView aria-label="Files"><TreeLinkItem id="src/Avatar.tsx" to="/src/Avatar.tsx">Avatar.tsx</TreeLinkItem><TreeLinkItem id="src/Button.tsx" to="/src/Button.tsx">Button.tsx</TreeLinkItem></TreeView></nav>)}
import {useRouter} from 'next/router'import Link from 'next/link'import {TreeView} from '@primer/react/drafts'function TreeLinkItem({id, href, children}) {const router = useRouter()const isCurrent = typeof href === 'string' ? router.asPath === href : router.pathname === href.pathnamereturn (<Link href={href} passHref><TreeView.Item id={id} aria-current={isCurrent ? 'page' : false} current={isCurrent}>{children}</TreeView.Item></Link>)}function App() {return (<nav aria-label="Files"><TreeView aria-label="Files"><TreeLinkItem id="src/Avatar.tsx" href="/src/Avatar.tsx">Avatar.tsx</TreeLinkItem><TreeLinkItemid="src/Button.tsx"// You can also pass a URL object// https://nextjs.org/docs/api-reference/next/link#with-url-objecthref={{pathname: '/src/[component].[filetype]',query: {component: 'Button', filetype: 'tsx'},}}>Button.tsx</TreeLinkItem></TreeView></nav>)}
function ControlledTreeView() {const [expanded, setExpanded] = React.useState(false)return (<Box sx={{display: 'grid', gap: 2, maxWidth: 400}}><Button onClick={() => setExpanded(!expanded)}>{expanded ? 'Collapse' : 'Expand'}</Button><nav aria-label="Files"><TreeView aria-label="Files"><TreeView.Item id="src" expanded={expanded} onExpandedChange={setExpanded}>src<TreeView.SubTree><TreeView.Item id="src/Avatar.tsx">Avatar.tsx</TreeView.Item><TreeView.Item id="src/Button.tsx" current>Button.tsx</TreeView.Item></TreeView.SubTree></TreeView.Item></TreeView></nav></Box>)}render(<ControlledTreeView />)
To render stateful visuals, pass a render function to TreeView.LeadingVisual
or TreeView.TrailingVisual
. The function will be called with the expanded
state of the item.
<Box sx={{maxWidth: 400}}><nav aria-label="Files"><TreeView aria-label="Files"><TreeView.Item id="src"><TreeView.LeadingVisual>{({isExpanded}) => (isExpanded ? <FileDirectoryOpenFillIcon /> : <FileDirectoryFillIcon />)}</TreeView.LeadingVisual>src<TreeView.SubTree><TreeView.Item id="src/Avatar.tsx">Avatar.tsx</TreeView.Item><TreeView.Item id="src/Button.tsx" current>Button.tsx</TreeView.Item></TreeView.SubTree></TreeView.Item></TreeView></nav></Box>
Since stateful directory icons are a common use case for TreeView, we provide a TreeView.DirectoryIcon
component for convenience. The previous example can be rewritten as:
<Box sx={{maxWidth: 400}}><nav aria-label="Files"><TreeView aria-label="Files"><TreeView.Item id="src"><TreeView.LeadingVisual><TreeView.DirectoryIcon /></TreeView.LeadingVisual>src<TreeView.SubTree><TreeView.Item id="src/Avatar.tsx">Avatar.tsx</TreeView.Item><TreeView.Item id="src/Button.tsx" current>Button.tsx</TreeView.Item></TreeView.SubTree></TreeView.Item></TreeView></nav></Box>
See Storybook for examples with asynchronously loaded items.
Name | Type | Default | Description |
---|---|---|---|
children Required | React.ReactNode |
Name | Type | Default | Description |
---|---|---|---|
children Required | React.ReactNode | ||
id Required | string | A unique identifier for the item. | |
current | boolean | false | Indicates whether the item is the current item. No more than one item should be current at once. The path to the current item will be expanded by default. |
defaultExpanded | boolean | The expanded state of the item when it is initially rendered. Use when you do not need to control the state. | |
expanded | boolean | The controlled expanded state of item. Must be used in conjunction with onExpandedChange. | |
containIntrinsicSize | string | The size of this item's contents. Passing this will set 'content-visiblity: auto' on the content container, delaying rendering until the item is in the viewport. | |
onExpandedChange | (expanded: boolean) => void | Event handler called when the expanded state of the item changes. | |
onSelect | (event: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>) => void | ||
ref | React.Ref<HTMLElement> | A ref to the element rendered by this component. |
Name | Type | Default | Description |
---|---|---|---|
children Required | | React.ReactNode | (props: {isExpanded: boolean}) => React.ReactNode) | ||
label | string | Provide an accessible label for the visual. This is not necessary for decorative visuals. |
Name | Type | Default | Description |
---|---|---|---|
children Required | | React.ReactNode | (props: {isExpanded: boolean}) => React.ReactNode) | ||
label | string | Provide an accessible label for the visual. This is not necessary for decorative visuals. |
Name | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | ||
state | | 'initial' | 'loading' | 'done' | 'error' | Specify a state if items in the subtree are loaded asynchronously. An asynchronous subtree can be in one of the following states: 'initial', 'loading', 'done', or 'error'. In the 'initial' state, items are neither loaded nor loading. In the 'loading' state, items are loading and the subtree will render a loading indicator. In the 'done' state, items are loaded. Screen readers will announce when a subtree enters the 'done' state. An 'error' state means that an error occured while loading items. | |
count | number | The number of items expected to be in the subtree. When in the loading state, the subtree will render a skeleton loading placeholder with the specified count of items |
Name | Type | Default | Description |
---|---|---|---|
children Required | React.ReactNode | The content of the dialog. This is usually a message explaining the error. | |
title | string | 'Error' | The title of the dialog. This is usually a short description of the error. |
onRetry | () => void | Event handler called when the user clicks the retry button. | |
onDismiss | () => void | Event handler called when the dialog is dismissed. |