import type { Meta, StoryObj } from '@storybook/react-vite'
import { IconSortDescending, IconSquareRounded } from '@tabler/icons-react'
import { useState } from 'react'
import { Button } from './Button'
import { CollapsibleSection } from './CollapsibleSection'
import { Divider } from './Divider'
import { Form } from './Form'
import { ListColumn } from './ListColumn'
import { NavItem } from './NavItem'
import { SectionHeader } from './SectionHeader'
import { SkeletonList } from './Skeleton'
import { TextInput } from './TextInput'
import { Toolbar, ToolbarButton } from './Toolbar'
/**
* The list column of a page: 290px, a line on its right, a header, and a list
* that scrolls. The rows are the app's own; these stories draw `NavItem`s as
* stand-ins.
*/
const meta = {
title: 'Layout/ListColumn',
component: ListColumn,
parameters: { layout: 'fullscreen' },
args: { title: 'Items', children: null },
argTypes: { children: { control: false }, actions: { control: false }, above: { control: false } },
// The column beside a main area, inside a surface — where it sits in the frame's card.
decorators: [
(Story) => (
{Story()}
),
],
} satisfies Meta
export default meta
type Story = StoryObj
const icon =
const names = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']
const rows = (n: number) =>
Array.from({ length: n }, (_, i) => )
const actions = (
{icon}
)
/** A list of one kind of row, 2px apart, with the column's actions in its header. */
export const Default: Story = {
args: { title: 'Items', actions },
render: (args) => {rows(6)},
}
/** `chevron`: a chevron after the title, as ContainerHeader draws it, for a column whose title opens something. */
export const WithChevron: Story = {
args: { title: 'Items', chevron: true, actions },
render: (args) => {rows(6)},
}
/**
* Groups. Each is headed by a package part, never by a row drawn by hand: a
* group that folds is a `CollapsibleSection`; a group that does not fold is a
* `SectionHeader` over its rows.
*/
export const Sections: Story = {
args: { title: 'Items' },
render: (args) => (
{rows(3)}
, tooltip: 'Sort by', onClick: () => {} }]}
/>
{rows(4)}
),
}
/** A row under the header that stays put while the list scrolls: a field that adds to the list. */
export const WithARowAbove: Story = {
parameters: { controls: { disable: true } },
render: function RowAbove() {
const [name, setName] = useState('')
return (
setName('')} layout="free" className="flex items-center gap-2 px-3 pt-3 pb-1">
setName(e.target.value)} placeholder="New item name" aria-label="New item name" />
}
>
{rows(5)}
)
},
}
/** More rows than fit: the list scrolls under a header that stays. */
export const ManyRows: Story = {
args: { title: 'Items', actions },
render: (args) => {rows(80)},
}
/** While the list is on its way: a skeleton shaped like the rows. */
export const Loading: Story = {
args: { title: 'Items' },
render: (args) => (
),
}
function Broken(): never {
throw new Error('A row could not draw itself')
}
/**
* When a row breaks: the column keeps its title and says so in the middle of
* the room the list had, with Try again. The rest of the page keeps working.
*/
export const WhenARowBreaks: Story = {
args: { title: 'Items', actions },
render: (args) => (
{rows(2)}
),
}
/** Closed with the rail: it narrows to nothing and fades. Press the button to watch it. */
export const Collapsing: Story = {
parameters: { controls: { disable: true } },
render: function Collapsing() {
const [collapsed, setCollapsed] = useState(false)
return (
<>
{rows(6)}