{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"stories","type":"registry:ui","title":"stories","description":"A composable Stories component - a carousel of individual video cards like you'd see on Facebook.","author":"Hayden Bleasel <hello@haydenbleasel.com>","dependencies":[],"devDependencies":[],"registryDependencies":["avatar","carousel"],"files":[{"type":"registry:ui","path":"index.tsx","content":"\"use client\";\n\nimport type {\n  ComponentProps,\n  HTMLAttributes,\n  VideoHTMLAttributes,\n} from \"react\";\nimport { useEffect, useRef } from \"react\";\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\";\nimport {\n  Carousel,\n  CarouselContent,\n  CarouselItem,\n} from \"@/components/ui/carousel\";\nimport { cn } from \"@/lib/utils\";\n\nexport type StoriesProps = ComponentProps<typeof Carousel>;\n\nexport const Stories = ({ className, opts, ...props }: StoriesProps) => (\n  <Carousel\n    className={cn(\"w-full\", className)}\n    opts={{\n      align: \"start\",\n      loop: false,\n      dragFree: true,\n      ...opts,\n    }}\n    {...props}\n  />\n);\n\nexport type StoriesContentProps = ComponentProps<typeof CarouselContent>;\n\nexport const StoriesContent = ({\n  className,\n  ...props\n}: StoriesContentProps) => (\n  <CarouselContent className={cn(\"-ml-2 gap-2\", className)} {...props} />\n);\n\nexport type StoryProps = HTMLAttributes<HTMLDivElement>;\n\nexport const Story = ({ className, ...props }: StoryProps) => (\n  <CarouselItem className={cn(\"basis-0 pl-2 md:pl-4\", className)}>\n    <div\n      className={cn(\n        \"group relative overflow-hidden rounded-xl bg-muted/40\",\n        \"cursor-pointer transition-all duration-200\",\n        \"hover:scale-[1.02] hover:shadow-lg\",\n        \"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n        className\n      )}\n      role=\"button\"\n      tabIndex={0}\n      {...props}\n    />\n  </CarouselItem>\n);\n\nexport type StoryVideoProps = VideoHTMLAttributes<HTMLVideoElement>;\n\nconst tRegex = /t=(\\d+(?:\\.\\d+)?)/;\n\nexport const StoryVideo = ({ className, ...props }: StoryVideoProps) => {\n  const videoRef = useRef<HTMLVideoElement>(null);\n  const initialTimeRef = useRef<number>(0);\n\n  // Parse the initial time from the src attribute (e.g., #t=20)\n  useEffect(() => {\n    const src = (props.src ?? \"\") as string;\n    let initialTime = 0;\n    if (typeof src === \"string\") {\n      const hashIndex = src.indexOf(\"#\");\n      if (hashIndex !== -1) {\n        const hash = src.slice(hashIndex + 1);\n        // Look for t=number or t=start,end\n        const tMatch = hash.match(tRegex);\n        if (tMatch) {\n          initialTime = Number.parseFloat(tMatch[1]);\n        }\n      }\n    }\n    initialTimeRef.current = initialTime;\n  }, [props.src]);\n\n  const handleMouseOver = () => {\n    videoRef.current?.play();\n  };\n\n  const handleMouseOut = () => {\n    if (videoRef.current) {\n      videoRef.current.pause();\n      videoRef.current.currentTime = initialTimeRef.current;\n    }\n  };\n\n  const handleFocus = () => {\n    videoRef.current?.play();\n  };\n\n  const handleBlur = () => {\n    if (videoRef.current) {\n      videoRef.current.pause();\n      videoRef.current.currentTime = initialTimeRef.current;\n    }\n  };\n\n  return (\n    <video\n      className={cn(\n        \"absolute inset-0 size-full object-cover\",\n        \"transition-opacity duration-200\",\n        \"group-hover:opacity-90\",\n        className\n      )}\n      loop\n      muted\n      onBlur={handleBlur}\n      onFocus={handleFocus}\n      onMouseOut={handleMouseOut}\n      onMouseOver={handleMouseOver}\n      preload=\"metadata\"\n      ref={videoRef}\n      tabIndex={0}\n      {...props}\n    />\n  );\n};\n\nexport type StoryImageProps = ComponentProps<\"img\"> & {\n  alt: string;\n};\n\nexport const StoryImage = ({ className, alt, ...props }: StoryImageProps) => (\n  // biome-ignore lint/performance/noImgElement: \"Kibo UI is framework agnostic\"\n  <img\n    alt={alt}\n    className={cn(\n      \"absolute inset-0 h-full w-full object-cover\",\n      \"transition-opacity duration-200\",\n      \"group-hover:opacity-90\",\n      className\n    )}\n    {...props}\n  />\n);\n\nexport type StoryAuthorProps = HTMLAttributes<HTMLDivElement>;\n\nexport const StoryAuthor = ({\n  className,\n  children,\n  ...props\n}: StoryAuthorProps) => (\n  <div\n    className={cn(\n      \"absolute right-0 bottom-0 left-0 z-10\",\n      \"p-3 text-white\",\n      className\n    )}\n    {...props}\n  >\n    <div className=\"flex items-center gap-2\">{children}</div>\n  </div>\n);\n\nexport type StoryAuthorImageProps = ComponentProps<typeof Avatar> & {\n  src?: string;\n  name?: string;\n  fallback?: string;\n};\n\nexport const StoryAuthorImage = ({\n  src,\n  fallback,\n  name,\n  className,\n  ...props\n}: StoryAuthorImageProps) => (\n  <Avatar className={cn(\"size-6 border border-white/20\", className)} {...props}>\n    {src && <AvatarImage alt={name} src={src} />}\n    <AvatarFallback className=\"bg-white/10 text-white text-xs\">\n      {fallback || name?.charAt(0)?.toUpperCase()}\n    </AvatarFallback>\n  </Avatar>\n);\n\nexport type StoryAuthorNameProps = HTMLAttributes<HTMLSpanElement>;\n\nexport const StoryAuthorName = ({\n  className,\n  ...props\n}: StoryAuthorNameProps) => (\n  <span className={cn(\"truncate font-medium text-sm\", className)} {...props} />\n);\n\nexport type StoryTitleProps = HTMLAttributes<HTMLDivElement>;\n\nexport const StoryTitle = ({ className, ...props }: StoryTitleProps) => (\n  <div\n    className={cn(\n      \"absolute top-0 right-0 left-0 z-10\",\n      \"p-3 text-white\",\n      className\n    )}\n    {...props}\n  />\n);\n\nexport type StoryOverlayProps = HTMLAttributes<HTMLDivElement> & {\n  side?: \"top\" | \"bottom\";\n};\n\nexport const StoryOverlay = ({\n  className,\n  side = \"bottom\",\n  ...props\n}: StoryOverlayProps) => {\n  const positionClasses =\n    side === \"top\" ? \"top-0 bg-gradient-to-b\" : \"bottom-0 bg-gradient-to-t\";\n\n  return (\n    <div\n      className={cn(\n        \"absolute right-0 left-0 h-10 from-black/20 to-transparent\",\n        positionClasses,\n        className\n      )}\n      {...props}\n    />\n  );\n};\n","target":"components/kibo-ui/stories/index.tsx"}],"css":{}}