{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "scrub-bar", "registryDependencies": [ "progress" ], "files": [ { "path": "components/ui/scrub-bar.tsx", "content": "\"use client\"\n\nimport * as React from \"react\"\nimport {\n createContext,\n useCallback,\n useContext,\n useRef,\n type ComponentProps,\n type HTMLAttributes,\n} from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Progress } from \"@/components/ui/progress\"\n\nfunction formatTimestamp(value: number) {\n if (!Number.isFinite(value) || value < 0) return \"0:00\"\n const totalSeconds = Math.floor(value)\n const minutes = Math.floor(totalSeconds / 60)\n const seconds = totalSeconds % 60\n return `${minutes}:${seconds.toString().padStart(2, \"0\")}`\n}\n\ninterface ScrubBarContextValue {\n duration: number\n value: number\n progress: number\n onScrub?: (time: number) => void\n onScrubStart?: () => void\n onScrubEnd?: () => void\n}\n\nconst ScrubBarContext = createContext(null)\n\nfunction useScrubBarContext() {\n const context = useContext(ScrubBarContext)\n if (!context) {\n throw new Error(\"useScrubBarContext must be used within a ScrubBar.Root\")\n }\n return context\n}\n\ninterface ScrubBarContainerProps extends HTMLAttributes {\n duration: number\n value: number\n onScrub?: (time: number) => void\n onScrubStart?: () => void\n onScrubEnd?: () => void\n}\n\nfunction ScrubBarContainer({\n duration,\n value,\n onScrub,\n onScrubStart,\n onScrubEnd,\n children,\n className,\n ...props\n}: ScrubBarContainerProps) {\n const progress = duration > 0 ? (value / duration) * 100 : 0\n\n const contextValue: ScrubBarContextValue = {\n duration,\n value,\n progress,\n onScrub,\n onScrubStart,\n onScrubEnd,\n }\n\n return (\n \n \n {children}\n \n \n )\n}\nScrubBarContainer.displayName = \"ScrubBarContainer\"\n\ntype ScrubBarTrackProps = HTMLAttributes\n\nfunction ScrubBarTrack({ className, children, ...props }: ScrubBarTrackProps) {\n const trackRef = useRef(null)\n const { duration, onScrub, onScrubStart, onScrubEnd, value } =\n useScrubBarContext()\n\n const getTimeFromClientX = useCallback(\n (clientX: number) => {\n const track = trackRef.current\n if (!track || !duration) return null\n const rect = track.getBoundingClientRect()\n const ratio = (clientX - rect.left) / rect.width\n const clamped = Math.min(Math.max(ratio, 0), 1)\n return duration * clamped\n },\n [duration]\n )\n\n const handlePointerDown = useCallback(\n (event: React.PointerEvent) => {\n if (!duration) return\n event.preventDefault()\n onScrubStart?.()\n const time = getTimeFromClientX(event.clientX)\n if (time != null) {\n onScrub?.(time)\n }\n\n const handleMove = (moveEvent: PointerEvent) => {\n const nextTime = getTimeFromClientX(moveEvent.clientX)\n if (nextTime != null) {\n onScrub?.(nextTime)\n }\n }\n\n const handleUp = () => {\n onScrubEnd?.()\n window.removeEventListener(\"pointermove\", handleMove)\n window.removeEventListener(\"pointerup\", handleUp)\n }\n\n window.addEventListener(\"pointermove\", handleMove)\n window.addEventListener(\"pointerup\", handleUp, { once: true })\n },\n [duration, getTimeFromClientX, onScrub, onScrubEnd, onScrubStart]\n )\n\n const clampedValue = Math.min(Math.max(value, 0), duration || 0)\n\n return (\n \n {children}\n \n )\n}\nScrubBarTrack.displayName = \"ScrubBarTrack\"\n\ntype ScrubBarProgressProps = Omit, \"value\">\n\nfunction ScrubBarProgress({ className, ...props }: ScrubBarProgressProps) {\n const { progress } = useScrubBarContext()\n\n return (\n div]:transition-none\", className)}\n {...props}\n />\n )\n}\nScrubBarProgress.displayName = \"ScrubBarProgress\"\n\ntype ScrubBarThumbProps = HTMLAttributes\n\nfunction ScrubBarThumb({ className, children, ...props }: ScrubBarThumbProps) {\n const { progress } = useScrubBarContext()\n return (\n \n {children}\n \n )\n}\nScrubBarThumb.displayName = \"ScrubBarThumb\"\n\ninterface ScrubBarTimeLabelProps extends HTMLAttributes {\n time: number\n format?: (time: number) => string\n}\n\nfunction ScrubBarTimeLabel({\n className,\n time,\n format = formatTimestamp,\n ...props\n}: ScrubBarTimeLabelProps) {\n return (\n \n {format(time)}\n \n )\n}\nScrubBarTimeLabel.displayName = \"ScrubBarTimeLabel\"\n\nexport {\n ScrubBarContainer,\n ScrubBarTrack,\n ScrubBarProgress,\n ScrubBarThumb,\n ScrubBarTimeLabel,\n}\n", "type": "registry:ui" } ], "type": "registry:ui" }