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 => (
))}
),
}