{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"selection-actions","type":"registry:component","title":"Selection Actions","description":"A floating bar of bulk actions that fades and rises in once a selection leaves zero, for pairing with Table or any selectable list.","author":"Ryan","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/selection-actions.tsx","type":"registry:component","target":"@components/motion/selection-actions.tsx","content":"\"use client\";\n// easeui.dev/components/motion/selection-actions\n\nimport { X } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport type { ReactNode } from \"react\";\nimport { EASE_OUT } from \"@/lib/ease\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface SelectionActionsProps {\n  /** How many items are selected. The bar hides itself at 0. */\n  count: number;\n  onClear?: () => void;\n  /** Action buttons, such as icon buttons for delete or tag. */\n  children: ReactNode;\n  className?: string;\n}\n\n/**\n * A bar for bulk actions that floats over the page rather than sitting in\n * flow, so rows in a Table (or any list) above it never jump when it\n * appears or disappears. Fixed to the bottom of the viewport, centered.\n * Fades and rises in once count leaves zero, and back out once the\n * selection clears.\n */\nexport function SelectionActions({ count, onClear, children, className }: SelectionActionsProps) {\n  const reduce = useReducedMotion();\n\n  return (\n    <AnimatePresence>\n      {count > 0 ? (\n        <motion.div\n          initial={reduce ? { opacity: 0 } : { opacity: 0, y: 8, scale: 0.97 }}\n          animate={{ opacity: 1, y: 0, scale: 1 }}\n          exit={reduce ? { opacity: 0 } : { opacity: 0, y: 8, scale: 0.97 }}\n          transition={{ duration: reduce ? 0.1 : 0.2, ease: EASE_OUT }}\n          className={cn(\n            \"fixed inset-x-0 bottom-6 z-40 mx-auto flex w-fit items-center gap-2 rounded-full bg-foreground py-1.5 pl-1.5 pr-3 text-background shadow-[0_12px_24px_-12px_rgb(0_0_0/0.4)]\",\n            className,\n          )}\n        >\n          <button\n            type=\"button\"\n            onClick={onClear}\n            aria-label=\"Clear selection\"\n            className=\"relative inline-flex h-7 w-7 shrink-0 touch-manipulation items-center justify-center rounded-full outline-none transition-colors duration-150 after:absolute after:-inset-1.5 hover:bg-background/15 focus-visible:ring-2 focus-visible:ring-background/60\"\n          >\n            <X aria-hidden=\"true\" className=\"h-3.5 w-3.5\" />\n          </button>\n          <span className=\"text-sm font-medium tabular-nums\">{count} selected</span>\n          <div className=\"mx-1 h-4 w-px bg-background/25\" />\n          <div className=\"flex items-center gap-1\">{children}</div>\n        </motion.div>\n      ) : null}\n    </AnimatePresence>\n  );\n}\n"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts","content":"// Shared motion tokens. Micro-interactions run 100 to 150ms, standard UI\n// 150 to 250ms, and panels up to 300ms.\n// Easing curves mirror the CSS custom properties in globals.css.\n\n/** ease-out-quint. Fast start that settles quickly. Entrances, exits, feedback. */\nexport const EASE_OUT = [0.23, 1, 0.32, 1] as const;\n/** ease-in-out-cubic. Elements already on screen moving to a new spot. */\nexport const EASE_IN_OUT = [0.645, 0.045, 0.355, 1] as const;\n/** Sheet and drawer glide. */\nexport const EASE_DRAWER = [0.32, 0.72, 0, 1] as const;\n\n/** CSS string form of EASE_OUT for inline style transitions. */\nexport const EASE_OUT_CSS = \"cubic-bezier(0.23, 1, 0.32, 1)\";\n\n// Springs are described by duration and bounce, which is easier to reason about\n// than stiffness and damping. Bounce stays at zero for product UI.\n\n/** Press feedback on buttons and other tappable surfaces. */\nexport const SPRING_PRESS = {\n  type: \"spring\",\n  duration: 0.15,\n  bounce: 0,\n} as const;\n\n/** Content swaps, label and icon slots trading places inside a control. */\nexport const SPRING_SWAP = {\n  type: \"spring\",\n  duration: 0.2,\n  bounce: 0,\n} as const;\n\n/** Overlay panel entrances, modals and sheets summoned by pointer. */\nexport const SPRING_PANEL = {\n  type: \"spring\",\n  duration: 0.25,\n  bounce: 0,\n} as const;\n\n/** Shared-layout glides, pills and indicators moving between positions. */\nexport const SPRING_LAYOUT = {\n  type: \"spring\",\n  duration: 0.22,\n  bounce: 0,\n} as const;\n\n/** Cursor-follow physics for decorative mouse tracking (magnetic, tilt). */\nexport const SPRING_MOUSE = {\n  stiffness: 320,\n  damping: 26,\n  mass: 0.3,\n} as const;\n\n/** Dragged handles and fills (sliders). Critically damped `useSpring` config,\n * so the value follows the pointer closely and never rebounds off an end. */\nexport const SPRING_GLIDE = {\n  stiffness: 700,\n  damping: 50,\n  mass: 0.5,\n} as const;\n"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"}]}