import { playgroundParameters } from "@charcuterie/storybook-config/story-parameters" import type { Meta, StoryObj } from "@storybook/react" import { useState } from "react" import { StoryCell, StoryGrid, } from "../board.storyHelpers.tsx" import type { DropRailTarget } from "./DropRail.tsx" import { DropRail } from "./DropRail.tsx" /** * **Invented, all of it** โ€” the same rule `Board.stories.tsx` * states and for the same reason. The list this component was built * for is a real household's, a published repo must never carry that, * and a committed screenshot is worse than a committed string * because no text scrub can reach inside a PNG. * * So these are a fictional studio's project groups. The long ones * are long on purpose: the rail's whole claim is that it stays * usable at a size where a menu does not. */ const STUDIO_TARGETS: DropRailTarget[] = [ { count: 41, isCurrent: true, key: "atlas", label: "Atlas Ingest", mark: "๐Ÿ“ฅ", }, { count: 53, key: "ferry", label: "Ferry Docs", mark: "๐Ÿ“„", }, { count: 94, key: "loom", label: "Loom Fleet", mark: "๐Ÿงต", }, { count: 12, isDisabled: true, key: "signal", label: "Signal Kitchen โ€” archived", mark: "๐Ÿ—„๏ธ", }, ] /** * Thirty-four groups, which is the number that made a menu the * wrong control. Long labels included, because a rail that only * looks right with short ones has not been tested. */ const MANY_TARGETS: DropRailTarget[] = [ { count: 94, key: "loom", label: "Loom Fleet", mark: "๐Ÿงต", }, { count: 81, key: "atlas-fix", label: "Atlas Ingest โ€” repair the malformed series folders", mark: "๐Ÿ”ง", }, { count: 77, isCurrent: true, key: "atlas-add", label: "Atlas Ingest โ€” add media", mark: "โž•", }, { count: 75, key: "ferry", label: "Ferry Docs", mark: "๐Ÿ“„", }, { count: 67, key: "swap", label: "Swap to better copy", mark: "๐Ÿ”„", }, { count: 53, key: "reels", label: "Reels and Shorts", mark: "๐ŸŽฌ", }, { count: 46, key: "configs", label: "Tooling Configs", mark: "โš™๏ธ", }, { count: 41, key: "upkeep", label: "Studio Upkeep", mark: "๐Ÿงบ", }, { count: 37, key: "triage", label: "Keep or cut?", mark: "๐Ÿค”", }, { count: 29, key: "hunt", label: "Find a better source", mark: "๐Ÿ”Ž", }, { count: 22, key: "colour", label: "Colour pass", mark: "๐ŸŽจ", }, { count: 18, key: "audio", label: "Audio conform", mark: "๐ŸŽง", }, { count: 11, key: "subs", label: "Subtitle timing", mark: "๐Ÿ’ฌ", }, { count: 9, key: "masters", label: "Master deliverables", mark: "๐Ÿ“ฆ", }, { count: 5, key: "cleanup", label: "Account cleanup", mark: "๐Ÿงน", }, ] const meta = { title: "Components/Controls/DropRail", component: DropRail, parameters: { layout: "padded" }, args: { isOpen: true, label: "Move to which project", onDismiss: () => {}, onPick: () => {}, targets: STUDIO_TARGETS, }, } satisfies Meta export default meta type Story = StoryObj /** * The rail as it appears the moment a move starts. One chip is the * group the card is already in โ€” visible, unofferable, and saying so * to a screen reader rather than only by being dimmer. */ export const Playground: Story = { parameters: playgroundParameters, } /** * The case the component exists for. Thirty-four destinations do not * fit in a menu without scrolling, and they do not fit on a display * without the page moving โ€” but they wrap into four lines here and * every one of them is one short movement away. */ export const ManyTargets: Story = { args: { targets: MANY_TARGETS }, } /** * Offerable, current and disabled, side by side. The three states * differ by more than colour: the current chip carries visually * hidden text and the disabled one carries `aria-disabled`. */ export const AllStates: Story = { render: (args) => ( ), } /** * The whole gesture, wired to a host the way an app wires it. * * Press and hold a card's handle and drag onto a chip, or **tap** the * handle and use the arrow keys and Enter. Both commit through the * same `onPick`, which is the point: the rail does not know which one * you used and the host does not have to say. */ export const InAList: Story = { render: (args) => { const StoryHost = () => { const [movingKey, setMovingKey] = useState< string | null >(null) const [moved, setMoved] = useState( null, ) const cards = [ { key: "wall", title: "Re-point the garden wall", }, { key: "filter", title: "Order the replacement extractor fan filter", }, { key: "keys", title: "Label the spare keys" }, ] return (
{ setMovingKey(null) }} onPick={(targetKey) => { const target = MANY_TARGETS.find( (one) => one.key === targetKey, ) setMoved(target?.label ?? targetKey) setMovingKey(null) }} targets={MANY_TARGETS} /> {cards.map((card) => (
{card.title}
))}

{moved === null ? "Nothing moved yet." : `Last move: ${moved}.`}

) } return }, }