{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "slide-to-unlock", "title": "Slide to Unlock", "author": "shakil-ahmed-billal ", "description": "Interactive slider inspired by the classic iPhone 'slide to unlock' gesture.", "dependencies": [ "motion" ], "registryDependencies": [ "https://xhakil.vercel.app/r/shimmering-text.json" ], "files": [ { "path": "src/registry/components/slide-to-unlock/slide-to-unlock.tsx", "content": "\"use client\"\r\n\r\nimport {\r\n animate,\r\n motion,\r\n type MotionValue,\r\n useMotionValue,\r\n useTransform,\r\n} from \"motion/react\"\r\nimport type { ComponentProps, ComponentPropsWithoutRef, JSX } from \"react\"\r\nimport { createContext, useCallback, useContext, useRef, useState } from \"react\"\r\n\r\nimport { cn } from \"@/lib/utils\"\r\n\r\ntype SlideToUnlockContextValue = {\r\n x: MotionValue\r\n trackRef: React.RefObject\r\n isDragging: boolean\r\n handleWidth: number\r\n textOpacity: MotionValue\r\n onDragStart: () => void\r\n onDragEnd: () => void\r\n}\r\n\r\nconst SlideToUnlockContext = createContext(\r\n null\r\n)\r\n\r\nfunction useSlideToUnlock() {\r\n const context = useContext(SlideToUnlockContext)\r\n if (!context) {\r\n throw new Error(\r\n `SlideToUnlock components must be used within SlideToUnlock`\r\n )\r\n }\r\n return context\r\n}\r\n\r\nexport type SlideToUnlockRootProps = ComponentProps<\"div\"> & {\r\n /**\r\n * Width of the drag handle in pixels.\r\n * @defaultValue 56\r\n * */\r\n handleWidth?: number\r\n /** Called when the handle is dragged fully to the end. */\r\n onUnlock?: () => void\r\n}\r\n\r\nexport function SlideToUnlock({\r\n className,\r\n handleWidth = 56,\r\n children,\r\n onUnlock,\r\n ...props\r\n}: SlideToUnlockRootProps) {\r\n const trackRef = useRef(null)\r\n const [isDragging, setIsDragging] = useState(false)\r\n const x = useMotionValue(0)\r\n\r\n const fadeDistance = handleWidth\r\n const textOpacity = useTransform(x, [0, fadeDistance], [1, 0])\r\n\r\n const handleDragStart = useCallback(() => {\r\n setIsDragging(true)\r\n }, [])\r\n\r\n const handleDragEnd = useCallback(() => {\r\n setIsDragging(false)\r\n\r\n const trackWidth = trackRef.current?.offsetWidth || 0\r\n const maxX = trackWidth - handleWidth\r\n\r\n if (x.get() >= maxX) {\r\n onUnlock?.()\r\n } else {\r\n animate(x, 0, { type: \"spring\", bounce: 0, duration: 0.25 })\r\n }\r\n }, [x, onUnlock, handleWidth])\r\n\r\n return (\r\n \r\n \r\n {children}\r\n \r\n \r\n )\r\n}\r\n\r\nexport type SlideToUnlockTrackProps = ComponentProps<\"div\">\r\n\r\nexport function SlideToUnlockTrack({\r\n className,\r\n children,\r\n ...props\r\n}: SlideToUnlockTrackProps) {\r\n const { trackRef } = useSlideToUnlock()\r\n\r\n return (\r\n \r\n {children}\r\n \r\n )\r\n}\r\n\r\nexport type SlideToUnlockTextProps = Omit<\r\n ComponentPropsWithoutRef,\r\n \"children\"\r\n> & {\r\n /**\r\n * Accepts a render function as `children` to react to the dragging state.\r\n *\r\n * @example\r\n * ```tsx\r\n * \r\n * {({ isDragging }) => {isDragging ? \"Release...\" : \"Slide to unlock\"}}\r\n * \r\n * ```\r\n */\r\n children: JSX.Element | ((props: { isDragging: boolean }) => JSX.Element)\r\n}\r\n\r\nexport function SlideToUnlockText({\r\n className,\r\n children,\r\n style,\r\n ...props\r\n}: SlideToUnlockTextProps) {\r\n const { handleWidth, textOpacity, isDragging } = useSlideToUnlock()\r\n\r\n return (\r\n \r\n {typeof children === \"function\" ? children({ isDragging }) : children}\r\n \r\n )\r\n}\r\n\r\nexport type SlideToUnlockHandleProps = ComponentPropsWithoutRef<\r\n typeof motion.div\r\n>\r\n\r\nexport function SlideToUnlockHandle({\r\n className,\r\n children,\r\n style,\r\n ...props\r\n}: SlideToUnlockHandleProps) {\r\n const {\r\n x,\r\n trackRef,\r\n onDragStart,\r\n onDragEnd,\r\n handleWidth: width,\r\n } = useSlideToUnlock()\r\n\r\n return (\r\n \r\n {children ?? (\r\n \r\n \r\n \r\n )}\r\n \r\n )\r\n}\r\n", "type": "registry:component" } ], "docs": "https://xhakil.vercel.app/components/slide-to-unlock", "type": "registry:component" }