{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "story",
  "title": "Story",
  "description": "Simillar to IG story, Story is a component that allows you to display a collection of components in a single view.",
  "files": [
    {
      "path": "registry/blocks/story/index.tsx",
      "content": "'use client';\nimport { cn } from '@/lib/utils';\nimport * as React from 'react';\nimport { Button, ButtonProps } from '@/components/systaliko-ui/shadcn/button';\nimport { PauseIcon, PlayIcon, ReplyIcon } from 'lucide-react';\n\ninterface StoryProps extends React.HTMLAttributes<HTMLDivElement> {\n  mediaLength: number;\n  duration?: number;\n}\ninterface StoryContextValue {\n  mediaLength: number;\n  currentIndex: number;\n  progress: number;\n  isPaused: boolean;\n  isEnded: boolean;\n  handleControl: () => void;\n  setCurrentIndex: (index: number) => void;\n  setIsPaused: (paused: boolean) => void;\n  setIsEnded: (ended: boolean) => void;\n}\nconst StoryContext = React.createContext<StoryContextValue | undefined>(\n  undefined,\n);\nfunction useStoryContext() {\n  const context = React.useContext(StoryContext);\n  if (context === undefined) {\n    throw new Error('useStoryContext must be used within a StoryProvider');\n  }\n  return context;\n}\nexport const Story = React.forwardRef<HTMLDivElement, StoryProps>(\n  ({ mediaLength, duration = 2000, className, children, ...props }, ref) => {\n    const [currentIndex, setCurrentIndex] = React.useState(0);\n    const [progress, setProgress] = React.useState(0);\n    const [isPaused, setIsPaused] = React.useState(false);\n    const [isEnded, setIsEnded] = React.useState(false);\n    const progressRef = React.useRef<number>(0);\n    const intervalRef = React.useRef<ReturnType<typeof setInterval> | null>(\n      null,\n    );\n\n    React.useEffect(() => {\n      progressRef.current = 0;\n      setProgress(0);\n    }, [currentIndex, duration, mediaLength]);\n    React.useEffect(() => {\n      if (mediaLength === 0 || isPaused) return;\n\n      if (intervalRef.current) {\n        clearInterval(intervalRef.current);\n        intervalRef.current = null;\n      }\n\n      const tick = 50;\n      const totalTicks = duration / tick;\n\n      intervalRef.current = setInterval(() => {\n        progressRef.current += 1;\n        const newProgress = (progressRef.current / totalTicks) * 100;\n        setProgress(newProgress);\n\n        if (progressRef.current >= totalTicks) {\n          clearInterval(intervalRef.current!);\n          intervalRef.current = null;\n\n          if (currentIndex < mediaLength - 1) {\n            setCurrentIndex((idx) => idx + 1);\n          } else {\n            setIsPaused(true);\n            setIsEnded(true);\n          }\n        }\n      }, tick);\n\n      return () => {\n        if (intervalRef.current) {\n          clearInterval(intervalRef.current);\n          intervalRef.current = null;\n        }\n      };\n    }, [isPaused, currentIndex, duration, mediaLength]);\n\n    if (mediaLength === 0) {\n      return (\n        <div className=\"text-center text-secondary\">No stories to display</div>\n      );\n    }\n\n    const handleControl = () => {\n      if (isEnded) {\n        setCurrentIndex(0);\n        setIsEnded(false);\n        setIsPaused(false);\n      } else {\n        setIsPaused((prev) => !prev);\n      }\n    };\n\n    return (\n      <StoryContext.Provider\n        value={{\n          mediaLength,\n          currentIndex,\n          progress,\n          isPaused,\n          isEnded,\n          handleControl,\n          setCurrentIndex,\n          setIsPaused,\n          setIsEnded,\n        }}\n      >\n        <div className={cn('mx-auto', className)} ref={ref} {...props}>\n          {children}\n        </div>\n      </StoryContext.Provider>\n    );\n  },\n);\nStory.displayName = 'Story';\n\nexport const StoryProgress = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & {\n    progressWrapClass?: string;\n    progressActiveClass?: string;\n  }\n>(({ className, progressWrapClass, progressActiveClass, ...props }, ref) => {\n  const {\n    mediaLength,\n    currentIndex,\n    progress,\n    setCurrentIndex,\n    setIsEnded,\n    setIsPaused,\n  } = useStoryContext();\n\n  const handleProgressClick = (index: number) => {\n    setCurrentIndex(index);\n    setIsPaused(false);\n    setIsEnded(false);\n  };\n\n  return (\n    <div className={cn('space-x-1 flex', className)} ref={ref} {...props}>\n      {Array.from({ length: mediaLength }).map((_, index) => {\n        const isActive = index === currentIndex;\n        const isCompleted = index < currentIndex;\n\n        return (\n          <div\n            key={index}\n            className={cn(\n              'h-1 flex-1 rounded bg-secondary cursor-pointer transition-colors',\n              'hover:bg-secondary/80',\n              progressWrapClass,\n            )}\n            onClick={() => handleProgressClick(index)}\n            role=\"button\"\n            tabIndex={0}\n            onKeyDown={(e) => {\n              if (e.key === 'Enter' || e.key === ' ') {\n                handleProgressClick(index);\n              }\n            }}\n          >\n            <div\n              className={cn(\n                'h-full rounded-[inherit] transition-all duration-200',\n                isActive\n                  ? 'bg-primary'\n                  : isCompleted\n                    ? 'bg-primary'\n                    : 'bg-transparent',\n                progressActiveClass,\n              )}\n              style={{\n                width: isActive ? `${progress}%` : isCompleted ? '100%' : '0%',\n              }}\n            />\n          </div>\n        );\n      })}\n    </div>\n  );\n});\nStoryProgress.displayName = 'StoryProgress';\n\nexport const StorySlide = React.forwardRef<\n  HTMLDivElement,\n  React.HTMLAttributes<HTMLDivElement> & { index: number }\n>(({ index, className, ...props }, ref) => {\n  const { currentIndex } = useStoryContext();\n  if (index !== currentIndex) return null;\n  return (\n    <div className={cn('animate-in fade-in', className)} ref={ref} {...props} />\n  );\n});\nStorySlide.displayName = 'StorySlide';\n\nexport const StoryControls: React.FC<ButtonProps> = ({\n  className,\n  ...props\n}) => {\n  const { isPaused, isEnded, handleControl } = useStoryContext();\n  return (\n    <Button\n      onClick={handleControl}\n      size=\"icon\"\n      {...props}\n      className={className}\n    >\n      {isPaused ? isEnded ? <ReplyIcon /> : <PlayIcon /> : <PauseIcon />}\n    </Button>\n  );\n};\nStoryControls.displayName = 'StoryControls';\n\nexport const StoryOverlay: React.FC = () => (\n  <div className=\" absolute inset-0 \">\n    <div className=\"absolute inset-x-0 top-0 h-24 bg-gradient-to-b from-black to-transparent\" />\n    <div className=\"absolute inset-x-0 bottom-0 h-40 bg-gradient-to-t from-black to-transparent\" />\n  </div>\n);\n",
      "type": "registry:block",
      "target": "components/systaliko-ui/blocks/story.tsx"
    }
  ],
  "type": "registry:block"
}