import type { Meta, StoryObj } from '@storybook/react-vite' import { IconSquareRounded } from '@tabler/icons-react' import { useState } from 'react' import { IconArrowUpRight, IconFolder, IconPlus } from '@tabler/icons-react' import { Chip } from './Chip' import { UnreadDot } from './UnreadDot' import { MenuItem, MenuSeparator } from './Menu' import { NavItem } from './NavItem' import { NavTree, NavTreeSection, type NavTreeGroup } from './NavTree' import { Sidebar } from './Sidebar' const placeholder = const hint = /** Three groups; the open one holds rows under rows, hints and counts, and long labels to see what fits. */ const GROUPS: NavTreeGroup[] = [ { id: 'g1', title: 'Group one', nodes: [{ id: 'a', label: 'Item two', href: '#', icon: placeholder }] }, { id: 'g2', title: 'A group with a longer name', nodes: [ { id: 'b', label: 'Item three', href: '#', icon: placeholder, count: 1, countLabel: '1 open' }, { id: 'c', label: 'An item whose label runs longer than the column', href: '#', icon: placeholder, children: [ { id: 'c1', label: 'An item one level in, also long', href: '#', icon: placeholder, count: 2, countLabel: '2 open' }, { id: 'c2', label: 'Item four', href: '#', icon: placeholder, children: [{ id: 'c21', label: 'An item two levels in, long too', href: '#', icon: placeholder }] }, ], }, { id: 'd', label: 'An item with a hint and a long label', href: '#', icon: placeholder, hint, count: 3, countLabel: '3 open' }, { id: 'e', label: 'Item five', href: '#', icon: placeholder, hint }, { id: 'f', label: 'An item with a hint and a count', href: '#', icon: placeholder, hint, count: 12, countLabel: '12 open' }, ], }, { id: 'g3', title: 'Group three', nodes: [{ id: 'g', label: 'Item six', href: '#', icon: placeholder }] }, ] const meta = { title: 'Frame/NavTree', component: NavTree, parameters: { layout: 'fullscreen' }, args: { title: 'Groups', groups: GROUPS, selected: 'b', emptyMessage: 'Nothing here yet.' }, argTypes: { groups: { control: false } }, // In the Sidebar, under a first row, as it sits in an app; a click marks the row it was on. render: function Render(args) { const [selected, setSelected] = useState(args.selected) return (
{ event.preventDefault() setSelected(node.id) }} />
) }, } satisfies Meta export default meta type Story = StoryObj /** Only the group holding the current row starts open. Point at a row with rows under it: its icon becomes the arrow. Point at a row with a hint: the chip shows. */ export const Default: Story = {} /** The current row is two levels in: its group opens, and the rows above it are open too. */ export const CurrentDeep: Story = { args: { selected: 'c21' } } /** No quiet label over the groups. */ export const NoTitle: Story = { args: { title: undefined } } /** The first read is on its way. */ export const Loading: Story = { args: { groups: null } } /** No groups: the caller's words, level with the rows. */ export const Empty: Story = { args: { groups: [] } } /** Close a group and reload the page: still closed. This browser remembers each group, under the key. */ export const Remembered: Story = { args: { storageKey: 'estiva-ui.stories.nav-tree' } } /** Actions beside the label and on each group, a group's dot, and unread rows — one urgent. */ export const WithActionsAndUnread: Story = { args: { titleActions: [{ icon: , tooltip: 'New', onClick: () => {} }], groups: [ { id: 'u1', title: 'Group one', trailing: , actions: [{ icon: , tooltip: 'Open', onClick: () => {} }], defaultOpen: true, nodes: [ { id: 'u1a', label: 'Item two', href: '#', icon: placeholder, unread: true }, { id: 'u1b', label: 'Item three', href: '#', icon: placeholder, unread: true, urgent: true }, { id: 'u1c', label: 'Item four', href: '#', icon: placeholder }, ] }, { id: 'u2', title: 'Group two', actions: [{ icon: , tooltip: 'Open', onClick: () => {} }], nodes: [{ id: 'u2a', label: 'Item five', href: '#', icon: placeholder }] }, ], selected: 'u1c', }, } const groupMenu = ( <> ) const rowMenu = ( <> ) /** A group's `menu`: its "More options", after its dot. Each row has its own. Hover a group's title. */ export const GroupMenus: Story = { args: { groups: [ { id: 'm1', title: 'Group one', trailing: , menu: groupMenu, defaultOpen: true, nodes: [ { id: 'm1a', label: 'Item two', href: '#', icon: placeholder, unread: true, menu: rowMenu }, { id: 'm1b', label: 'Item three', href: '#', icon: placeholder, menu: rowMenu }, ] }, { id: 'm2', title: 'Group two', menu: groupMenu, nodes: [{ id: 'm2a', label: 'Item four', href: '#', icon: placeholder, menu: rowMenu }] }, ], selected: 'm1b', }, } const folder = /** A group's `icon`: a folder before its title, the arrow in its place on hover; its rows start under the title. */ export const GroupIcons: Story = { args: { groups: [ { id: 'i1', title: 'Group one', icon: folder, menu: groupMenu, defaultOpen: true, nodes: [ { id: 'i1a', label: 'Item two', href: '#', icon: placeholder, unread: true, menu: rowMenu }, { id: 'i1b', label: 'Item three', href: '#', icon: placeholder, menu: rowMenu }, ] }, { id: 'i2', title: 'Group two', icon: folder, menu: groupMenu, nodes: [{ id: 'i2a', label: 'Item four', href: '#', icon: placeholder }] }, ], selected: 'i1b', }, } /** Read as it opens: a group still being read, a group with nothing in it, a row whose rows are read when it opens, and one that could not be read. */ export const ReadAsItOpens: Story = { args: { groups: [ { id: 'l1', title: 'Being read', defaultOpen: true, nodes: null }, { id: 'l2', title: 'Empty', defaultOpen: true, nodes: [], message: 'Nothing in this yet.' }, { id: 'l3', title: 'Rows read on open', defaultOpen: true, nodes: [ { id: 'l3a', label: 'Item with rows not read yet', href: '#', icon: placeholder, hasChildren: true, defaultOpen: false }, { id: 'l3b', label: 'Item whose rows failed', href: '#', icon: placeholder, hasChildren: true, childrenMessage: 'This could not be read.' }, ] }, ], selected: undefined, }, } /** Groups the app draws itself, as `NavTree`'s children: each a `NavTreeSection` with `NavItem` rows — for groups and rows that each read their own data. */ export const AppDrawnGroups: Story = { args: { groups: undefined }, render: function Render(args) { return (
} menu={groupMenu}>
) }, }