{
  "name": "stagger-children",
  "type": "registry:ui",
  "description": "Offset children onto their own sequences, in forward, reverse, centre or edge order, in and out",
  "dependencies": [
    "remotion"
  ],
  "registryDependencies": [
    "motion-primitive",
    "timing"
  ],
  "files": [
    {
      "path": "registry/bases/default/primitives/stagger-children.tsx",
      "type": "registry:ui",
      "content": "import {\n  Children,\n  cloneElement,\n  isValidElement,\n  type ReactElement,\n  type ReactNode,\n} from \"react\";\nimport { Sequence, useVideoConfig } from \"remotion\";\nimport {\n  defaultExitFrames,\n  DEFAULT_ENTER_FRAMES,\n  type EnterExitOptions,\n} from \"@/remotion/lib/motion-primitive\";\nimport { staggerDelay } from \"@/remotion/lib/timing\";\n\nexport type StaggerOrder = \"forward\" | \"reverse\" | \"center\" | \"edges\";\n\nexport type StaggerChildrenProps = {\n  children: ReactNode;\n  /** Frames between one child starting and the next. */\n  staggerInFrames?: number;\n  /** Frames before the first child starts. */\n  baseDelayInFrames?: number;\n  /**\n   * Which child goes first. `center` runs outwards from the middle, `edges`\n   * runs inwards — both read as one gesture across a row rather than a queue.\n   */\n  order?: StaggerOrder;\n  /**\n   * Frames between one child leaving and the next, in the order they arrived.\n   * Only affects children that animate out; 0 lands the group together.\n   */\n  exitStaggerInFrames?: number;\n};\n\n/** Position in the run order, 0 = first to start. */\nfunction rankOf(index: number, count: number, order: StaggerOrder): number {\n  const mid = (count - 1) / 2;\n\n  switch (order) {\n    case \"reverse\":\n      return count - 1 - index;\n    case \"center\":\n      return Math.round(Math.abs(index - mid));\n    case \"edges\":\n      return Math.round(mid - Math.abs(index - mid));\n    default:\n      return index;\n  }\n}\n\n/** Host elements would render an unknown attribute; only components take props. */\nfunction acceptsMotionProps(child: ReactElement): boolean {\n  return typeof child.type !== \"string\";\n}\n\n/**\n * Offsets each child onto its own slot with `<Sequence layout=\"none\">`, so\n * every child animates from its local frame 0 and needs no delay of its own.\n *\n * Inside a bounded window each slot is given an end as well, which is what\n * lets children with `exit` land out on time. `exitStaggerInFrames` moves each\n * child's exit earlier instead of shortening its slot — cutting slots short\n * would unmount children one by one and collapse the layout under them.\n *\n * @see skills/remotion/remotion-markup/sequencing.md\n */\nexport const StaggerChildren: React.FC<StaggerChildrenProps> = ({\n  children,\n  staggerInFrames = 8,\n  baseDelayInFrames = 0,\n  order = \"forward\",\n  exitStaggerInFrames = 0,\n}) => {\n  const { durationInFrames: windowFrames } = useVideoConfig();\n  const items = Children.toArray(children);\n  const bounded = Number.isFinite(windowFrames);\n\n  return (\n    <>\n      {items.map((child, index) => {\n        if (!isValidElement(child)) {\n          return child;\n        }\n\n        const rank = rankOf(index, items.length, order);\n        const from = staggerDelay(rank, staggerInFrames, baseDelayInFrames);\n        const slot = bounded ? Math.max(1, windowFrames - from) : undefined;\n\n        /* First in, first out: the wave leaves in the order it arrived. */\n        const lag = (items.length - 1 - rank) * exitStaggerInFrames;\n        const element =\n          lag > 0 && slot !== undefined && acceptsMotionProps(child)\n            ? cloneElement(child, staggeredExit(child, slot, lag))\n            : child;\n\n        return (\n          <Sequence\n            key={child.key ?? `stagger-${index}`}\n            from={from}\n            durationInFrames={slot}\n            layout=\"none\"\n          >\n            {element}\n          </Sequence>\n        );\n      })}\n    </>\n  );\n};\n\n/** Exit start that lands this child `lag` frames before the end of its slot. */\nfunction staggeredExit(\n  child: ReactElement,\n  slot: number,\n  lag: number,\n): EnterExitOptions {\n  const props = child.props as EnterExitOptions;\n  if (props.exitAtInFrames !== undefined) return {};\n\n  const exitFrames =\n    props.exitInFrames ??\n    defaultExitFrames(props.durationInFrames ?? DEFAULT_ENTER_FRAMES);\n\n  return { exitAtInFrames: Math.max(0, slot - exitFrames - lag) };\n}\n"
    }
  ],
  "atlas": {
    "lane": "atoms",
    "drive": "time",
    "tier": "core",
    "tags": [
      "sequence"
    ]
  }
}