Comparison Slider
trovecn/comparison-sliderFreeComponent
Draggable clip-path divider that reveals one live layer over another.
Install it
npx shadcn@latest add https://trovecn.dev/r/comparison-slider.jsonSource
1"use client";23import { useCallback, useRef, useState, type PointerEvent as ReactPointerEvent } from "react";4import { ChevronsLeftRight } from "lucide-react";56import { cn } from "@/lib/utils";78export interface ComparisonSliderProps {9 /** Layer revealed on the left of the divider. */10 before: React.ReactNode;11 /** Layer revealed on the right of the divider — sits underneath, full-bleed. */12 after: React.ReactNode;13 beforeLabel?: string;14 afterLabel?: string;15 /** Divider position as a percentage (0-100) of the container width. */16 defaultValue?: number;17 className?: string;18}1920const STEP = 4;21const RESET_TRANSITION = "clip-path 300ms cubic-bezier(0.23, 1, 0.32, 1)";2223export function ComparisonSlider({24 before,25 after,26 beforeLabel = "Before",27 afterLabel = "After",28 defaultValue = 50,29 className,30}: ComparisonSliderProps) {31 const [value, setValue] = useState(defaultValue);32 const [isSettling, setIsSettling] = useState(false);33 const containerRef = useRef<HTMLDivElement>(null);34 const draggingRef = useRef(false);3536 const updateFromClientX = useCallback((clientX: number) => {37 const el = containerRef.current;38 if (!el) return;39 const rect = el.getBoundingClientRect();40 const pct = ((clientX - rect.left) / rect.width) * 100;41 setValue(Math.min(100, Math.max(0, pct)));42 }, []);4344 function onHandlePointerDown(event: ReactPointerEvent<HTMLDivElement>) {45 draggingRef.current = true;46 setIsSettling(false);47 event.currentTarget.setPointerCapture(event.pointerId);48 updateFromClientX(event.clientX);49 }5051 function onHandlePointerMove(event: ReactPointerEvent<HTMLDivElement>) {52 if (!draggingRef.current) return;53 updateFromClientX(event.clientX);54 }5556 function onHandlePointerUp(event: ReactPointerEvent<HTMLDivElement>) {57 draggingRef.current = false;58 event.currentTarget.releasePointerCapture(event.pointerId);59 }6061 function onHandleKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {62 if (event.key === "ArrowLeft") setValue((v) => Math.max(0, v - STEP));63 else if (event.key === "ArrowRight") setValue((v) => Math.min(100, v + STEP));64 else if (event.key === "Home") setValue(0);65 else if (event.key === "End") setValue(100);66 else return;67 setIsSettling(true);68 event.preventDefault();69 }7071 function onDoubleClick() {72 draggingRef.current = false;73 setIsSettling(true);74 setValue(50);75 }7677 return (78 <div79 ref={containerRef}80 className={cn(81// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from trovecn
All 26 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | trovecn | Components | Free | 3 deps | |
| Agent Activityagent-activity | trovecn | Components | Free | 2 deps | |
| Approval Requestapproval-request | trovecn | Components | Free | 2 deps | |
| Buttonbutton | trovecn | Components | Free | 2 deps | |
| Checkboxcheckbox | trovecn | Components | Free | 2 deps | |
| Checkbox Groupcheckbox-group | trovecn | Components | Free | 2 deps | |
| Comboboxcombobox | trovecn | Components | Free | 3 deps | |
| Context Menucontext-menu | trovecn | Components | Free | 3 deps |
