import type { Meta, StoryObj } from '@storybook/react-vite' import { IconMenu2 } from '@tabler/icons-react' import { useState, type ReactNode } from 'react' import { IconButton } from './IconButton' import { PersonTrigger } from './PersonTrigger' import { TopBar } from './TopBar' /** * The bar needs its full width, and it needs content to stand against: * the solid manner sits above its content, the floating manner floats * over it — no border, no background, clicks passing through. */ const meta = { title: 'Frame/TopBar', component: TopBar, parameters: { layout: 'fullscreen', // axe color-contrast is off here until PLAN.md stage 0.10 is ruled: // the placeholder content under the bar is muted text, 3.94:1 on --bg-base in // signal (AA 4.5:1). a11y: { config: { rules: [{ id: 'color-contrast', enabled: false }] } }, }, argTypes: { variant: { control: 'inline-radio', options: ['solid', 'floating'] }, menu: { control: false }, logo: { control: false }, search: { control: false }, onSearch: { control: false }, right: { control: false }, }, } satisfies Meta export default meta type Story = StoryObj const face = const menuButton = ( ) /** Solid sits above its content: bar first, page below. */ const Below = ({ children }: { children: ReactNode }) => (
{children}
content below the bar
) /** Floating stands over its content: the page runs to the top edge underneath. */ const Behind = ({ children }: { children: ReactNode }) => (
{children}
content under the bar
) /** The in-flow manner: hairline, surface, the name on the left. */ export const Solid: Story = { render: (args) => ( {}} right={face} /> ), } /** * The bar's own search field: a click on it, or Ctrl+K (Cmd+K on a Mac) * anywhere, calls `onSearch` — here it counts, where an app opens its launcher. */ export const Search: Story = { render: function Search(args) { const [opened, setOpened] = useState(0) return (
setOpened((n) => n + 1)} searchPlaceholder="Search Estiva…" right={face} />
{opened === 0 ? 'Click the field, or press Ctrl+K.' : `Search opened ${opened} ${opened === 1 ? 'time' : 'times'}.`}
) }, } /** No search yet: the centre slot holds its place, so it drops in later without moving anything. */ export const SolidNoSearch: Story = { render: (args) => ( ), } /** The overlay manner: no border, no background, and only the clusters catch the pointer. */ export const Floating: Story = { render: (args) => ( {}} right={face} /> ), } /** Floating without a search — menu button and identity alone. */ export const FloatingNoSearch: Story = { render: (args) => ( ), } /** The menu button and the logo side by side — an app whose rail collapses keeps its name next to the way to collapse it. */ export const MenuAndLogo: Story = { render: (args) => ( ), }