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