import type { Meta, StoryObj } from '@storybook/nextjs-vite' import { MarqueeWall, useMarqueeSync } from '../components/ui/marquee-wall' // Arbitrary-children demo content: simple lockup tiles (the wall is // content-agnostic — chips, logos, feed rows all ride the same engine). const TILES = [ 'Datto RMM', 'ConnectWise', 'HaloPSA', 'SentinelOne', 'Proofpoint', 'Veeam', 'Auvik', 'IT Glue', ] function Tile({ label }: { label: string }) { return (
{label}
) } const meta: Meta = { title: 'UI/MarqueeWall', component: MarqueeWall, parameters: { layout: 'padded', docs: { description: { component: 'THE endless-scroll wall container — a clipped area whose content loops as a marquee along either axis, fading where the clip cuts. Transform-driven on the shared `useMarqueeEngine` core (no inner scroller). Auto-static when the content fits or under `prefers-reduced-motion`. `useMarqueeSync` pins multiple walls to one position driver; `trackId` publishes live offsets for FLIP-morph coordination.', }, }, }, decorators: [ (Story) => (
), ], } export default meta type Story = StoryObj /** Horizontal loop of arbitrary children with the default right fade. */ export const Horizontal: Story = { render: () => ( {TILES.map(t => ( ))} ), } /** Vertical loop (bottom fade → bottom→top travel) inside a height cap. */ export const Vertical: Story = { render: () => (
{TILES.map(t => ( ))}
), } /** Two walls pinned to ONE position driver via `useMarqueeSync` — scroll * stays pixel-locked (the FreeTrialCTA resolve-strip mechanism). */ export const SyncedPair: Story = { render: function SyncedPairStory() { const sync = useMarqueeSync() return (
{[0, 1].map(row => ( {TILES.map(t => ( ))} ))}
) }, } /** Plain mode — the consumer's opt-out: same wall, no motion, no fades. */ export const Plain: Story = { render: () => ( {TILES.map(t => ( ))} ), } /** Reverse travel (content moves right) with a custom-size leading fade. */ export const Reversed: Story = { render: () => ( {TILES.map(t => ( ))} ), }