import type { Meta, StoryObj } from '@storybook/react-vite' import { IconArrowBackUp, IconArrowForwardUp, IconArrowUpRight, IconBold, IconItalic, IconLink, IconSquareRounded, IconUnderline } from '@tabler/icons-react' import { useState } from 'react' import { IconButton } from './IconButton' import { Popover } from './Popover' import { Toolbar, ToolbarButton, ToolbarInput, ToolbarLink, ToolbarSeparator } from './Toolbar' /** * A strip of controls that behaves as **one** control: Tab in, arrow keys * along, Tab out. * * It draws the elevated box a floating strip needs — the same one a `Menu` * draws — so a toolbar over a card, a paragraph or an image is separated from * it without the caller drawing anything. */ const meta = { title: 'Primitives/Toolbar', component: Toolbar, args: { 'aria-label': 'Formatting', children: null }, argTypes: { children: { control: false } }, } satisfies Meta export default meta type Story = StoryObj const icon = /** Four controls, one Tab stop. */ export const Default: Story = { render: (args) => ( {icon} {icon} {icon} {icon} ), } /** Groups, divided. The separator carries the role as well as the hairline. */ export const Grouped: Story = { parameters: { controls: { disable: true } }, render: () => ( ), } /** * A field in the strip. While it has focus the arrow keys are the caret's — * which is what a field is for — and Tab resumes the walk. That is why a field * in a toolbar is `ToolbarInput` rather than a `TextInput` dropped in the row. */ export const WithAField: Story = { parameters: { controls: { disable: true } }, render: function WithField() { const [url, setUrl] = useState('') return ( setUrl(e.target.value)} placeholder="Paste a link" aria-label="Link address" className="h-7 w-48" /> ) }, } /** * A control that cannot be used **keeps its place in the walk**. A strip whose * controls come and go from the arrow keys as their state changes is a strip * you cannot learn — and a `disabledReason` you cannot reach is a reason * nobody reads. */ export const WithADisabledControl: Story = { parameters: { controls: { disable: true } }, render: () => ( {icon} {icon} {icon} ), } /** * A control that goes somewhere, beside controls that do something. The link * has the buttons' look and joins the same walk; it has a real address, so a * Ctrl+click opens a new tab. */ export const WithALink: Story = { parameters: { controls: { disable: true } }, render: () => ( {icon} ), } /** Down rather than across: ↑ and ↓ walk it. */ export const Vertical: Story = { parameters: { controls: { disable: true } }, render: () => ( {icon} {icon} {icon} ), } /** * **Inside something that already draws a box** — a `Popover`, a dialog, a * card's own panel — the strip drops its own with `surface={false}`. Two * boxes inside each other is the tell. */ export const OnAnExistingSurface: Story = { parameters: { controls: { disable: true }, layout: 'fullscreen' }, render: () => (
{icon}} ariaLabel="Formatting" /* Above the control that opened it: a strip acts on what is under it. `side` is the preference; Base UI flips it when there is no room. */ side="top" className="w-auto min-w-0" contentClassName="p-1" > {icon} {icon} {icon}
), }