{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-smartwatch",
  "type": "registry:ui",
  "title": "Animated Smartwatch",
  "description": "An Animated Smartwatch component for displaying a smartwatch animation.",
  "files": [
    {
      "path": "src/registry/ui/animated-smartwatch.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport { useMemo, useRef, useSyncExternalStore } from \"react\";\nimport {\n  motion,\n  useMotionTemplate,\n  useReducedMotion,\n  useScroll,\n  useTransform,\n} from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\nconst CASE_COLORS = {\n  midnight: \"bg-gradient-to-b from-neutral-700 to-neutral-900\",\n  silver: \"bg-gradient-to-b from-neutral-200 to-neutral-400\",\n  starlight: \"bg-gradient-to-b from-stone-100 to-stone-300\",\n} as const;\n\nconst BAND_COLORS = {\n  midnight: \"bg-neutral-800\",\n  silver: \"bg-neutral-300\",\n  starlight: \"bg-stone-200\",\n  primary: \"bg-primary\",\n} as const;\n\nexport type SmartwatchColor = keyof typeof CASE_COLORS;\nexport type SmartwatchBandColor = keyof typeof BAND_COLORS;\n\n// Clock store: ticks every second on the client, `null` on the server so the\n// first client render matches the server HTML.\nfunction subscribeToClock(onChange: () => void) {\n  const id = window.setInterval(onChange, 1000);\n  return () => window.clearInterval(id);\n}\nconst getMinute = () => Math.floor(Date.now() / 60_000);\nconst getServerMinute = () => null;\n\nfunction ClockFace() {\n  const minute = useSyncExternalStore(subscribeToClock, getMinute, getServerMinute);\n  const formatters = useMemo(\n    () => ({\n      time: new Intl.DateTimeFormat(undefined, { hour: \"numeric\", minute: \"2-digit\" }),\n      date: new Intl.DateTimeFormat(undefined, { weekday: \"short\", day: \"numeric\" }),\n    }),\n    []\n  );\n  const now = minute === null ? null : new Date(minute * 60_000);\n\n  return (\n    <div className=\"flex size-full flex-col items-center justify-center gap-1 bg-black text-white\">\n      <time\n        dateTime={now?.toISOString()}\n        className=\"text-[calc(var(--watch-size)*0.17)] leading-none font-semibold tracking-tight tabular-nums\"\n      >\n        {now ? formatters.time.format(now) : \"--:--\"}\n      </time>\n      <span className=\"text-[calc(var(--watch-size)*0.07)] font-medium text-white/60\">\n        {now ? formatters.date.format(now) : \" \"}\n      </span>\n    </div>\n  );\n}\n\nexport type AnimatedSmartwatchProps = React.ComponentProps<\"div\"> & {\n  /** Screen content. Defaults to a built-in clock face. */\n  children?: React.ReactNode;\n  /** Length of the scroll track, any CSS length. */\n  scrollHeight?: string;\n  /** Case color preset. */\n  color?: SmartwatchColor;\n  /** Band color preset. Defaults to the case color. */\n  bandColor?: SmartwatchBandColor;\n  /** Width of the watch case in px. */\n  size?: number;\n};\n\n/**\n * A watch mockup that rotates into a resting pose as it scrolls into view,\n * then stays pinned for the rest of the scroll track.\n */\nexport function AnimatedSmartwatch({\n  children,\n  scrollHeight = \"150vh\",\n  color = \"midnight\",\n  bandColor,\n  size = 200,\n  className,\n  style,\n  ...props\n}: AnimatedSmartwatchProps) {\n  const containerRef = useRef<HTMLDivElement>(null);\n  const reduceMotion = useReducedMotion();\n\n  const { scrollYProgress } = useScroll({\n    target: containerRef,\n    offset: [\"start end\", \"end end\"],\n  });\n\n  const rotateZ = useTransform(scrollYProgress, [0, 0.8], [-30, 0]);\n  const rotateX = useTransform(scrollYProgress, [0, 0.8], [20, 0]);\n  const rotateY = useTransform(scrollYProgress, [0, 0.8], [-15, 0]);\n  const scale = useTransform(scrollYProgress, [0, 0.8], [0.85, 1]);\n  const brightness = useTransform(scrollYProgress, [0, 0.8], [0.6, 1]);\n  const screenFilter = useMotionTemplate`brightness(${brightness})`;\n\n  const animated = !reduceMotion;\n\n  return (\n    <div\n      ref={containerRef}\n      data-slot=\"animated-smartwatch\"\n      className={cn(\"relative w-full\", className)}\n      style={\n        {\n          height: scrollHeight,\n          \"--watch-size\": `${size}px`,\n          ...style,\n        } as React.CSSProperties\n      }\n      {...props}\n    >\n      <div className=\"sticky top-0 flex h-svh items-center justify-center overflow-x-clip perspective-[1200px]\">\n        <motion.div\n          className=\"flex flex-col items-center transform-3d\"\n          style={animated ? { rotateZ, rotateX, rotateY, scale } : undefined}\n        >\n          <div\n            aria-hidden\n            className={cn(\n              \"h-[calc(var(--watch-size)*0.55)] w-[calc(var(--watch-size)*0.7)] rounded-t-[calc(var(--watch-size)*0.12)]\",\n              BAND_COLORS[bandColor ?? color]\n            )}\n          />\n\n          <div\n            className={cn(\n              \"relative z-10 -my-[calc(var(--watch-size)*0.04)] aspect-[4/5] w-[var(--watch-size)] rounded-[calc(var(--watch-size)*0.24)] p-[calc(var(--watch-size)*0.045)] ring-1 ring-black/10\",\n              CASE_COLORS[color]\n            )}\n          >\n            <div\n              aria-hidden\n              className={cn(\n                \"absolute top-[28%] -right-[calc(var(--watch-size)*0.035)] h-[20%] w-[calc(var(--watch-size)*0.045)] rounded-r-sm\",\n                CASE_COLORS[color]\n              )}\n            />\n            <motion.div\n              className=\"relative size-full overflow-hidden rounded-[calc(var(--watch-size)*0.2)] bg-black\"\n              style={animated ? { filter: screenFilter } : undefined}\n            >\n              {children ?? <ClockFace />}\n            </motion.div>\n          </div>\n\n          <div\n            aria-hidden\n            className={cn(\n              \"h-[calc(var(--watch-size)*0.55)] w-[calc(var(--watch-size)*0.7)] rounded-b-[calc(var(--watch-size)*0.12)]\",\n              BAND_COLORS[bandColor ?? color]\n            )}\n          />\n        </motion.div>\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "dependencies": [
    "motion"
  ]
}
