{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "animated-icons-table",
  "title": "Animated Icons Table",
  "description": "Row actions built with Iconimate animated icons (favorite, notify, archive, delete) that animate on hover, focus, and click instead of static glyphs.",
  "dependencies": [
    "@tanstack/react-table",
    "motion"
  ],
  "registryDependencies": [
    "table",
    "button"
  ],
  "files": [
    {
      "path": "src/lib/animated-icon.ts",
      "content": "import { useCallback, useEffect, useRef } from 'react'\nimport { useAnimation, useReducedMotion } from 'motion/react'\nimport type { DOMAttributes, HTMLAttributes } from 'react'\nimport type { Transition } from 'motion/react'\n\n/**\n * Shared runtime for the Iconimate-derived animated icons in `#/components/ui/icons`.\n * Each icon file only defines its own motion variants + markup; this holds the\n * boilerplate every one of them would otherwise repeat (adapted from\n * https://github.com/smammar100/Iconimate).\n */\n\n/** Imperative handle every icon exposes — lets consumers trigger motion on touch, where `:hover` never fires. */\nexport interface IconHandle {\n  startAnimation: () => void\n  stopAnimation: () => void\n}\n\nexport interface IconProps extends HTMLAttributes<HTMLDivElement> {\n  /** Rendered width & height in px. Defaults to 28; the set is calibrated to read at 24 (ship size). */\n  size?: number\n}\n\n/** A cubic-bezier easing curve. */\nexport type Bezier = [number, number, number, number]\n\n/** Decelerate-to-rest with an expo-out tail — things landing / arriving. */\nexport const ARRIVE: Bezier = [0.16, 1, 0.3, 1]\n\n/** Gentle standard glide — used by every \"normal\" variant for hover-out. */\nexport const RETURN: Bezier = [0.4, 0, 0.2, 1]\n\n/** Exaggeration — an ease that overshoots its target then eases back. */\nexport const OVERSHOOT_BACK: Bezier = [0.34, 1.56, 0.64, 1]\n\n/** Duration scale in seconds, calibrated for legibility at the 24px ship size. */\nexport const DUR = { instant: 0.12, fast: 0.2, base: 0.32, slow: 0.5 } as const\n\n/** The canonical hover-out transition, spread into every \"normal\" variant. */\nexport const RETURN_TRANSITION: Transition = { duration: DUR.base, ease: RETURN }\n\n/** Anticipation — the \"wind-up\" scale dip taken just before a pop. */\nexport const ANTICIPATE_DIP = 0.92\n\n/** A ready \"pop in\": a wind-up dip then an overshoot peak. Spread the result and add your own `normal` rest state. */\nexport function popIn({\n  dip = ANTICIPATE_DIP,\n  peak = 1.18,\n  duration = DUR.slow,\n}: { dip?: number; peak?: number; duration?: number } = {}): {\n  scale: number[]\n  transition: Transition\n} {\n  return {\n    scale: [1, dip, peak, 1],\n    transition: { duration, ease: ARRIVE, times: [0, 0.25, 0.6, 1] },\n  }\n}\n\ntype AnimationControls = ReturnType<typeof useAnimation>\n\nexport interface HoverController {\n  controls: AnimationControls\n  /** True when the icon should render its static fallback instead of animating. */\n  reduced: boolean\n  /** True when motion may repeat on its own (the replay loop below). */\n  ambient: boolean\n  start: () => void\n  stop: () => void\n  /** Spread onto the icon's wrapper. Keyboard focus triggers it too, not just pointer. */\n  bind: Pick<DOMAttributes<Element>, 'onMouseEnter' | 'onMouseLeave' | 'onFocus' | 'onBlur'>\n}\n\n/**\n * The common-case hover controller: one `useAnimation` instance plus enter / leave /\n * focus / blur wiring, looping the \"animate\" variant while hovered/focused.\n */\nexport function useHover(): HoverController {\n  const controls = useAnimation()\n  const reduced = false\n  const ambient = !(useReducedMotion() ?? false)\n\n  const looping = useRef(false)\n  const replayTimer = useRef<number | undefined>(undefined)\n\n  const start = useCallback(() => {\n    if (looping.current) return\n    looping.current = true\n    const run = () => {\n      if (!looping.current) return\n      const t0 = performance.now()\n      void controls.start('animate').then(() => {\n        if (!looping.current) return\n        controls.set('normal')\n        if (!ambient) {\n          looping.current = false\n          return\n        }\n        const elapsed = performance.now() - t0\n        replayTimer.current = window.setTimeout(run, elapsed < 100 ? 300 : elapsed * 0.3)\n      })\n    }\n    run()\n  }, [controls, ambient])\n\n  const stop = useCallback(() => {\n    looping.current = false\n    window.clearTimeout(replayTimer.current)\n    void controls.start('normal')\n  }, [controls])\n\n  useEffect(\n    () => () => {\n      looping.current = false\n      window.clearTimeout(replayTimer.current)\n    },\n    [],\n  )\n\n  return {\n    controls,\n    reduced,\n    ambient,\n    start,\n    stop,\n    bind: { onMouseEnter: start, onMouseLeave: stop, onFocus: start, onBlur: stop },\n  }\n}\n",
      "type": "registry:lib",
      "target": "@components/tables/animated-icons-table/lib/animated-icon.ts"
    },
    {
      "path": "src/components/ui/icons/star-icon.tsx",
      "content": "'use client'\n\nimport { forwardRef, useImperativeHandle } from 'react'\nimport { motion } from 'motion/react'\nimport type { Variants } from 'motion/react'\nimport { ARRIVE, RETURN_TRANSITION, popIn, useHover } from '#/lib/animated-icon.ts'\nimport type { IconHandle, IconProps } from '#/lib/animated-icon.ts'\n\n// One confident turn, paired with an anticipation dip and an overshoot pop.\nconst pop = popIn({ peak: 1.18, duration: 0.6 })\nconst twinkle: Variants = {\n  normal: { rotate: 0, scale: 1, transition: RETURN_TRANSITION },\n  animate: {\n    rotate: [0, 360],\n    scale: pop.scale,\n    transition: {\n      rotate: { duration: 0.7, ease: ARRIVE },\n      scale: pop.transition,\n    },\n  },\n}\n\nexport const StarIcon = forwardRef<IconHandle, IconProps>(function StarIcon(\n  { size = 28, style, ...props },\n  ref,\n) {\n  const { controls, reduced, start, stop, bind } = useHover()\n  useImperativeHandle(ref, () => ({ startAnimation: start, stopAnimation: stop }), [start, stop])\n\n  return (\n    <div {...props} {...bind} style={{ display: 'inline-flex', overflow: 'hidden', ...style }}>\n      <motion.svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width={size}\n        height={size}\n        viewBox=\"0 0 256 256\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth={18}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        initial=\"normal\"\n        animate={controls}\n        style={{ overflow: 'visible' }}\n      >\n        <motion.path\n          variants={reduced ? undefined : twinkle}\n          style={{ transformBox: 'view-box', transformOrigin: '128px 132px' }}\n          d=\"M128 36 150 98 216 100 163 139 182 202 128 165 74 202 93 139 40 100 106 98 Z\"\n        />\n      </motion.svg>\n    </div>\n  )\n})\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/icons/star-icon.tsx"
    },
    {
      "path": "src/components/ui/icons/bell-ringing-icon.tsx",
      "content": "'use client'\n\nimport { forwardRef, useImperativeHandle } from 'react'\nimport { motion } from 'motion/react'\nimport type { Variants } from 'motion/react'\nimport { RETURN_TRANSITION, useHover } from '#/lib/animated-icon.ts'\nimport type { IconHandle, IconProps } from '#/lib/animated-icon.ts'\n\n// RING + EMIT — the bell rocks, the clapper trails it, and sound leaves as two\n// wavefronts that travel outward and fade, twice over the gesture.\nconst ARC_R =\n  'M224,71.1a8,8,0,0,1-10.78-3.42,94.13,94.13,0,0,0-33.46-36.91,8,8,0,1,1,8.54-13.54,111.46,111.46,0,0,1,39.12,43.09A8,8,0,0,1,224,71.1Z'\nconst ARC_L =\n  'M35.71,72a8,8,0,0,0,7.1-4.32A94.13,94.13,0,0,1,76.27,30.77a8,8,0,1,0-8.54-13.54A111.46,111.46,0,0,0,28.61,60.32,8,8,0,0,0,35.71,72Z'\nconst SHELL =\n  'M221.81,175.94A16,16,0,0,1,208,200H48a16,16,0,0,1-13.79-24.06C43.22,160.39,48,138.28,48,112a80,80,0,0,1,160,0C208,138.27,212.78,160.38,221.81,175.94Z' +\n  'M208,184c-10.64-18.27-16-42.49-16-72a64,64,0,0,0-128,0c0,29.52-5.38,53.74-16,72Z'\nconst CLAPPER = 'M167.2,200a40,40,0,0,1-78.4,0L105.38,200a24,24,0,0,0,45.24,0Z'\n// Full original glyph, for the reduced-motion static render.\nconst BELL_RINGING =\n  'M224,71.1a8,8,0,0,1-10.78-3.42,94.13,94.13,0,0,0-33.46-36.91,8,8,0,1,1,8.54-13.54,111.46,111.46,0,0,1,39.12,43.09A8,8,0,0,1,224,71.1ZM35.71,72a8,8,0,0,0,7.1-4.32A94.13,94.13,0,0,1,76.27,30.77a8,8,0,1,0-8.54-13.54A111.46,111.46,0,0,0,28.61,60.32,8,8,0,0,0,35.71,72Zm186.1,103.94A16,16,0,0,1,208,200H167.2a40,40,0,0,1-78.4,0H48a16,16,0,0,1-13.79-24.06C43.22,160.39,48,138.28,48,112a80,80,0,0,1,160,0C208,138.27,212.78,160.38,221.81,175.94ZM150.62,200H105.38a24,24,0,0,0,45.24,0ZM208,184c-10.64-18.27-16-42.49-16-72a64,64,0,0,0-128,0c0,29.52-5.38,53.74-16,72Z'\n\nconst CROWN = { transformBox: 'view-box' as const, originX: 0.5, originY: 32 / 256 }\nconst DOME = { transformBox: 'view-box' as const, originX: 0.5, originY: 112 / 256 }\n\nconst SWING = 12\nconst TRAVEL = 16\nconst LOUD = 1.14\n\nconst shell: Variants = {\n  normal: { rotate: 0, transition: RETURN_TRANSITION },\n  animate: {\n    rotate: [0, -11, SWING, -9.5, 7.4, -2.5, 0],\n    transition: { duration: 0.85, times: [0, 0.2, 0.44, 0.64, 0.8, 0.92, 1], ease: 'easeInOut' },\n  },\n}\n\nconst clapper: Variants = {\n  normal: { x: 0, transition: RETURN_TRANSITION },\n  animate: {\n    x: [0, -TRAVEL, TRAVEL, -13, 9, -3.5, 0],\n    transition: { duration: 0.85, times: [0, 0.24, 0.48, 0.68, 0.84, 0.94, 1], ease: 'easeInOut' },\n  },\n}\n\nconst arcs: Variants = {\n  normal: { scale: 1, opacity: 1, transition: RETURN_TRANSITION },\n  animate: {\n    scale: [1, LOUD, 1, LOUD, 1],\n    opacity: [1, 0.25, 1, 0.35, 1],\n    transition: { duration: 0.85, times: [0, 0.22, 0.44, 0.66, 1], ease: 'easeInOut' },\n  },\n}\n\nexport const BellRingingIcon = forwardRef<IconHandle, IconProps>(function BellRingingIcon(\n  { size = 28, style, ...props },\n  ref,\n) {\n  const { controls, reduced, start, stop, bind } = useHover()\n  useImperativeHandle(ref, () => ({ startAnimation: start, stopAnimation: stop }), [start, stop])\n\n  if (reduced) {\n    return (\n      <div {...props} {...bind} style={{ display: 'inline-flex', overflow: 'hidden', ...style }}>\n        <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 256 256\" fill=\"currentColor\">\n          <path d={BELL_RINGING} />\n        </svg>\n      </div>\n    )\n  }\n\n  return (\n    <div {...props} {...bind} style={{ display: 'inline-flex', overflow: 'hidden', ...style }}>\n      <motion.svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width={size}\n        height={size}\n        viewBox=\"0 0 256 256\"\n        fill=\"currentColor\"\n        initial=\"normal\"\n        animate={controls}\n        style={{ overflow: 'visible' }}\n      >\n        {/* The arcs sit outside the shell group: sound doesn't swing with the bell. */}\n        <motion.g variants={arcs} style={DOME}>\n          <path d={ARC_L} />\n          <path d={ARC_R} />\n        </motion.g>\n\n        <motion.g variants={shell} style={CROWN}>\n          <path d={SHELL} />\n          <motion.path d={CLAPPER} variants={clapper} />\n        </motion.g>\n      </motion.svg>\n    </div>\n  )\n})\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/icons/bell-ringing-icon.tsx"
    },
    {
      "path": "src/components/ui/icons/archive-icon.tsx",
      "content": "'use client'\n\nimport { forwardRef, useImperativeHandle } from 'react'\nimport { motion } from 'motion/react'\nimport type { Variants } from 'motion/react'\nimport { OVERSHOOT_BACK, RETURN_TRANSITION, useHover } from '#/lib/animated-icon.ts'\nimport type { IconHandle, IconProps } from '#/lib/animated-icon.ts'\n\n// STASH — the lid lifts and tilts open on a left-edge hinge, a label drops into the\n// box, then the lid swings shut with a small squash on impact.\nconst LID = 'M32,48H224a16,16,0,0,1,16,16V88a16,16,0,0,1-16,16H32a16,16,0,0,1-16-16V64A16,16,0,0,1,32,48ZM32,64H224V88H32Z'\nconst BOX = 'M32,104H224V192a16,16,0,0,1-16,16H48a16,16,0,0,1-16-16ZM48,104H208V192H48Z'\nconst SLOT = 'M96,136a8,8,0,0,1,8-8h48a8,8,0,0,1,0,16H104A8,8,0,0,1,96,136Z'\n\nconst BOX_BOTTOM = { transformBox: 'view-box' as const, originX: 0.5, originY: 0.81 }\nconst LID_HINGE = { transformBox: 'view-box' as const, originX: 0.0625, originY: 0.406 }\n\nconst lid: Variants = {\n  normal: { y: 0, rotate: 0, transition: RETURN_TRANSITION },\n  animate: {\n    y: [0, -24, -24, 0, 0],\n    rotate: [0, -10, -10, 0, 0],\n    transition: { duration: 1.2, times: [0, 0.22, 0.58, 0.82, 1], ease: 'easeInOut' },\n  },\n}\nconst label: Variants = {\n  normal: { y: 0, opacity: 1, transition: RETURN_TRANSITION },\n  animate: {\n    y: [-26, -26, 0, 0],\n    opacity: [0, 0, 1, 1],\n    transition: { duration: 1.2, times: [0, 0.3, 0.58, 1], ease: OVERSHOOT_BACK },\n  },\n}\nconst box: Variants = {\n  normal: { scaleY: 1, transition: RETURN_TRANSITION },\n  animate: {\n    scaleY: [1, 1, 0.93, 1.02, 1],\n    transition: { duration: 1.2, times: [0, 0.78, 0.86, 0.94, 1], ease: 'easeOut' },\n  },\n}\n\nexport const ArchiveIcon = forwardRef<IconHandle, IconProps>(function ArchiveIcon(\n  { size = 28, style, ...props },\n  ref,\n) {\n  const { controls, reduced, start, stop, bind } = useHover()\n  useImperativeHandle(ref, () => ({ startAnimation: start, stopAnimation: stop }), [start, stop])\n\n  return (\n    <div {...props} {...bind} style={{ display: 'inline-flex', overflow: 'hidden', ...style }}>\n      <motion.svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width={size}\n        height={size}\n        viewBox=\"0 0 256 256\"\n        fill=\"currentColor\"\n        initial=\"normal\"\n        animate={controls}\n        style={{ overflow: 'visible' }}\n      >\n        <motion.g variants={reduced ? undefined : box} style={BOX_BOTTOM}>\n          <path d={BOX} fillRule=\"evenodd\" />\n        </motion.g>\n        <motion.path d={SLOT} variants={reduced ? undefined : label} />\n        <motion.path d={LID} fillRule=\"evenodd\" variants={reduced ? undefined : lid} style={LID_HINGE} />\n      </motion.svg>\n    </div>\n  )\n})\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/icons/archive-icon.tsx"
    },
    {
      "path": "src/components/ui/icons/trash-icon.tsx",
      "content": "'use client'\n\nimport { forwardRef, useImperativeHandle } from 'react'\nimport { motion } from 'motion/react'\nimport type { Variants } from 'motion/react'\nimport { RETURN_TRANSITION, useHover } from '#/lib/animated-icon.ts'\nimport type { IconHandle, IconProps } from '#/lib/animated-icon.ts'\n\n// One-shot \"toss\": the lid swings up on its left hinge and lifts clear of the rim,\n// then drops back down with a small bounce.\nconst lid: Variants = {\n  normal: { rotate: 0, y: 0, transition: RETURN_TRANSITION },\n  animate: {\n    rotate: [0, -22, 7, -3, 0],\n    y: [0, -10, 2, -1, 0],\n    transition: { duration: 0.7, ease: 'easeInOut', times: [0, 0.32, 0.62, 0.82, 1] },\n  },\n}\n\nexport const TrashIcon = forwardRef<IconHandle, IconProps>(function TrashIcon(\n  { size = 28, style, ...props },\n  ref,\n) {\n  const { controls, reduced, start, stop, bind } = useHover()\n  useImperativeHandle(ref, () => ({ startAnimation: start, stopAnimation: stop }), [start, stop])\n\n  return (\n    <div {...props} {...bind} style={{ display: 'inline-flex', overflow: 'hidden', ...style }}>\n      <motion.svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width={size}\n        height={size}\n        viewBox=\"0 0 256 256\"\n        fill=\"none\"\n        stroke=\"currentColor\"\n        strokeWidth={18}\n        strokeLinecap=\"round\"\n        strokeLinejoin=\"round\"\n        initial=\"normal\"\n        animate={controls}\n        style={{ overflow: 'visible' }}\n      >\n        {/* can body: rounded-bottom bin that tapers in, with two vertical ribs */}\n        <path d=\"M74 92l7 110a16 16 0 0 0 16 15h62a16 16 0 0 0 16-15l7-110\" />\n        <path d=\"M112 120v72\" />\n        <path d=\"M144 120v72\" />\n        {/* lid: rim bar + small handle, hinges and lifts from its left end */}\n        <motion.g\n          variants={reduced ? undefined : lid}\n          style={{ transformBox: 'view-box', transformOrigin: '56px 70px' }}\n        >\n          <path d=\"M56 70h144\" />\n          <path d=\"M104 70V58a10 10 0 0 1 10-10h28a10 10 0 0 1 10 10v12\" />\n        </motion.g>\n      </motion.svg>\n    </div>\n  )\n})\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/icons/trash-icon.tsx"
    },
    {
      "path": "src/components/ui/icons/arrows-clockwise-icon.tsx",
      "content": "'use client'\n\nimport { forwardRef, useImperativeHandle, useRef } from 'react'\nimport { animate, motion, useMotionValue, useReducedMotion } from 'motion/react'\nimport { ARRIVE } from '#/lib/animated-icon.ts'\nimport type { IconHandle, IconProps } from '#/lib/animated-icon.ts'\n\n// PULSE — a spin about the centre carrying a squash-and-pop scale as secondary\n// action, a tactile refresh tap. The glyph's 2-fold rotational symmetry lands a\n// full 360° turn seamlessly back at rest.\nconst GLYPH =\n  'M224,48V96a8,8,0,0,1-8,8H168a8,8,0,0,1,0-16h28.69L182.06,73.37a79.56,79.56,0,0,0-56.13-23.43h-.45A79.52,79.52,0,0,0,69.59,72.71,8,8,0,0,1,58.41,61.27a96,96,0,0,1,135,.79L208,76.69V48a8,8,0,0,1,16,0ZM186.41,183.29a80,80,0,0,1-112.47-.66L59.31,168H88a8,8,0,0,0,0-16H40a8,8,0,0,0-8,8v48a8,8,0,0,0,16,0V179.31l14.63,14.63A95.43,95.43,0,0,0,130,222.06h.53a95.36,95.36,0,0,0,67.07-27.33,8,8,0,0,0-11.18-11.44Z'\n\nexport const ArrowsClockwiseIcon = forwardRef<IconHandle, IconProps>(function ArrowsClockwiseIcon(\n  { size = 28, style, ...props },\n  ref,\n) {\n  const reduced = useReducedMotion() ?? false\n  const rotate = useMotionValue(0)\n  const scale = useMotionValue(1)\n  const rAnim = useRef<ReturnType<typeof animate> | null>(null)\n  const sAnim = useRef<ReturnType<typeof animate> | null>(null)\n\n  const start = () => {\n    if (reduced) return\n    rAnim.current?.stop()\n    sAnim.current?.stop()\n    rotate.set(0)\n    scale.set(1)\n    rAnim.current = animate(rotate, 360, { duration: 0.85, ease: ARRIVE })\n    sAnim.current = animate(scale, [1, 0.9, 1.06, 1], { duration: 0.7, ease: 'easeOut', times: [0, 0.3, 0.65, 1] })\n  }\n\n  const stop = () => {\n    rAnim.current?.stop()\n    sAnim.current?.stop()\n    rotate.set(0)\n    scale.set(1)\n  }\n\n  useImperativeHandle(ref, () => ({ startAnimation: start, stopAnimation: stop }))\n\n  return (\n    <div\n      {...props}\n      onMouseEnter={start}\n      onMouseLeave={stop}\n      onFocus={start}\n      onBlur={stop}\n      style={{ display: 'inline-flex', ...style }}\n    >\n      <motion.svg\n        xmlns=\"http://www.w3.org/2000/svg\"\n        width={size}\n        height={size}\n        viewBox=\"0 0 256 256\"\n        fill=\"currentColor\"\n        style={{ rotate, scale, overflow: 'visible' }}\n      >\n        <path d={GLYPH} />\n      </motion.svg>\n    </div>\n  )\n})\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/icons/arrows-clockwise-icon.tsx"
    },
    {
      "path": "src/components/animated-icons/columns.tsx",
      "content": "export type TeamMember = {\n  id: string\n  name: string\n  email: string\n  role: string\n  favorite: boolean\n  notify: boolean\n  archived: boolean\n}\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/columns.tsx"
    },
    {
      "path": "src/components/animated-icons/data-table.tsx",
      "content": "'use client'\n\nimport { useMemo, useRef } from 'react'\nimport { flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'\nimport type { ColumnDef } from '@tanstack/react-table'\n\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '#/components/ui/table'\nimport { Button } from '#/components/ui/button'\nimport { ArchiveIcon } from '#/components/ui/icons/archive-icon.tsx'\nimport { ArrowsClockwiseIcon } from '#/components/ui/icons/arrows-clockwise-icon.tsx'\nimport { BellRingingIcon } from '#/components/ui/icons/bell-ringing-icon.tsx'\nimport { StarIcon } from '#/components/ui/icons/star-icon.tsx'\nimport { TrashIcon } from '#/components/ui/icons/trash-icon.tsx'\nimport type { IconHandle } from '#/lib/animated-icon.ts'\nimport { cn } from '#/lib/utils.ts'\nimport type { TeamMember } from './columns'\n\ninterface AnimatedIconsTableProps {\n  data: TeamMember[]\n  onDataChange: (updater: (prev: TeamMember[]) => TeamMember[]) => void\n  onReset: () => void\n}\n\nexport function AnimatedIconsTable({ data, onDataChange, onReset }: AnimatedIconsTableProps) {\n  const refreshRef = useRef<IconHandle>(null)\n\n  function toggle(id: string, key: 'favorite' | 'notify' | 'archived') {\n    onDataChange((prev) => prev.map((row) => (row.id === id ? { ...row, [key]: !row[key] } : row)))\n  }\n\n  function remove(id: string) {\n    onDataChange((prev) => prev.filter((row) => row.id !== id))\n  }\n\n  function handleReset() {\n    refreshRef.current?.startAnimation()\n    onReset()\n  }\n\n  const columns = useMemo<ColumnDef<TeamMember>[]>(\n    () => [\n      {\n        accessorKey: 'name',\n        header: 'Name',\n        cell: ({ row }) => (\n          <span className={cn(row.original.archived && 'text-muted-foreground line-through')}>\n            {row.original.name}\n          </span>\n        ),\n      },\n      { accessorKey: 'email', header: 'Email' },\n      { accessorKey: 'role', header: 'Role' },\n      {\n        id: 'actions',\n        header: () => <span className=\"sr-only\">Actions</span>,\n        cell: ({ row }) => {\n          const member = row.original\n          return (\n            <div className=\"flex items-center justify-end gap-1\">\n              <button\n                type=\"button\"\n                aria-label={member.favorite ? 'Unfavorite' : 'Favorite'}\n                aria-pressed={member.favorite}\n                title={member.favorite ? 'Unfavorite' : 'Favorite'}\n                onClick={() => toggle(member.id, 'favorite')}\n                className={cn(\n                  'rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground',\n                  member.favorite && 'text-amber-500 hover:text-amber-500',\n                )}\n              >\n                <StarIcon size={18} style={{ fill: member.favorite ? 'currentColor' : 'none' }} />\n              </button>\n              <button\n                type=\"button\"\n                aria-label={member.notify ? 'Mute notifications' : 'Notify'}\n                aria-pressed={member.notify}\n                title={member.notify ? 'Mute notifications' : 'Notify'}\n                onClick={() => toggle(member.id, 'notify')}\n                className={cn(\n                  'rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground',\n                  member.notify && 'text-blue-500 hover:text-blue-500',\n                )}\n              >\n                <BellRingingIcon size={18} />\n              </button>\n              <button\n                type=\"button\"\n                aria-label={member.archived ? 'Unarchive' : 'Archive'}\n                aria-pressed={member.archived}\n                title={member.archived ? 'Unarchive' : 'Archive'}\n                onClick={() => toggle(member.id, 'archived')}\n                className={cn(\n                  'rounded-md p-1.5 text-muted-foreground hover:bg-muted hover:text-foreground',\n                  member.archived && 'text-foreground',\n                )}\n              >\n                <ArchiveIcon size={18} />\n              </button>\n              <button\n                type=\"button\"\n                aria-label=\"Delete\"\n                title=\"Delete\"\n                onClick={() => remove(member.id)}\n                className=\"rounded-md p-1.5 text-muted-foreground hover:bg-destructive/10 hover:text-destructive\"\n              >\n                <TrashIcon size={18} />\n              </button>\n            </div>\n          )\n        },\n      },\n    ],\n    [],\n  )\n\n  const table = useReactTable({\n    data,\n    columns,\n    getCoreRowModel: getCoreRowModel(),\n  })\n\n  return (\n    <div>\n      <div className=\"mb-3 flex flex-wrap items-center justify-between gap-2\">\n        <p className=\"text-sm text-muted-foreground\">\n          Hover a row action to preview its motion; click to apply it.\n        </p>\n        <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={handleReset}>\n          <ArrowsClockwiseIcon ref={refreshRef} size={16} />\n          Reset\n        </Button>\n      </div>\n      <div className=\"overflow-hidden rounded-md border\">\n        <Table>\n          <TableHeader>\n            {table.getHeaderGroups().map((headerGroup) => (\n              <TableRow key={headerGroup.id}>\n                {headerGroup.headers.map((header) => (\n                  <TableHead key={header.id} className={header.id === 'actions' ? 'text-right' : undefined}>\n                    {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}\n                  </TableHead>\n                ))}\n              </TableRow>\n            ))}\n          </TableHeader>\n          <TableBody>\n            {table.getRowModel().rows.length ? (\n              table.getRowModel().rows.map((row) => (\n                <TableRow key={row.id} className={cn(row.original.archived && 'bg-muted/40')}>\n                  {row.getVisibleCells().map((cell) => (\n                    <TableCell key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</TableCell>\n                  ))}\n                </TableRow>\n              ))\n            ) : (\n              <TableRow>\n                <TableCell colSpan={columns.length} className=\"h-24 text-center\">\n                  No team members. Click Reset to restore the demo data.\n                </TableCell>\n              </TableRow>\n            )}\n          </TableBody>\n        </Table>\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/data-table.tsx"
    },
    {
      "path": "src/components/animated-icons/index.tsx",
      "content": "import { useState } from 'react'\n\nimport { AnimatedIconsTable } from './data-table'\nimport type { TeamMember } from './columns'\n\nconst initialData: TeamMember[] = [\n  { id: '1', name: 'Amelia Frost', email: 'amelia@acme.dev', role: 'Engineering', favorite: true, notify: true, archived: false },\n  { id: '2', name: 'Noah Park', email: 'noah@acme.dev', role: 'Design', favorite: false, notify: true, archived: false },\n  { id: '3', name: 'Sofia Reyes', email: 'sofia@acme.dev', role: 'Product', favorite: false, notify: false, archived: false },\n  { id: '4', name: 'Liam Chen', email: 'liam@acme.dev', role: 'Engineering', favorite: false, notify: false, archived: true },\n  { id: '5', name: 'Maya Okafor', email: 'maya@acme.dev', role: 'Support', favorite: true, notify: false, archived: false },\n]\n\nexport function AnimatedIconsTableDemo() {\n  const [data, setData] = useState(initialData)\n\n  return (\n    <AnimatedIconsTable\n      data={data}\n      onDataChange={setData}\n      onReset={() => setData(initialData)}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/tables/animated-icons-table/index.tsx"
    }
  ],
  "type": "registry:block"
}