[comunidad] · animate-ui / motion / lucide-react
PinList
Lista con reordenamiento fluido: fija ítems con el pin y se mueven entre secciones con spring.
animate-uimotionlucide-react
@/components/animate-ui/components/community/pin-listPinList
RadialChart
Gráfica circular
Dock
Navegación flotante
All Items
PinList
Lista con fijados
CodeTabs
Tabs de código
SparklesText
Texto con destellos
RadialChart
Gráfica circular
Dock
Navegación flotante
All Items
PinList
Lista con fijados
CodeTabs
Tabs de código
SparklesText
Texto con destellos
1'use client';;2import * as React from 'react';3import { Pin } from 'lucide-react';4import { motion, LayoutGroup, AnimatePresence } from 'motion/react';5import { cn } from '@/lib/utils';6 7function PinList({8 items,9 labels = { pinned: 'Pinned Items', unpinned: 'All Items' },10 transition = { stiffness: 320, damping: 20, mass: 0.8, type: 'spring' },11 12 labelMotionProps = {13 initial: { opacity: 0 },14 animate: { opacity: 1 },15 exit: { opacity: 0 },16 transition: { duration: 0.22, ease: 'easeInOut' },17 },18 19 className,20 labelClassName,21 pinnedSectionClassName,22 unpinnedSectionClassName,23 zIndexResetDelay = 500,24 ...props25}) {26 const [listItems, setListItems] = React.useState(items);27 const [togglingGroup, setTogglingGroup] = React.useState(null);28 29 const pinned = listItems.filter((u) => u.pinned);30 const unpinned = listItems.filter((u) => !u.pinned);31 32 const toggleStatus = (id) => {33 const item = listItems.find((u) => u.id === id);34 if (!item) return;35 36 setTogglingGroup(item.pinned ? 'pinned' : 'unpinned');37 setListItems((prev) => {38 const idx = prev.findIndex((u) => u.id === id);39 if (idx === -1) return prev;40 const updated = [...prev];41 const [item] = updated.splice(idx, 1);42 if (!item) return prev;43 const toggled = { ...item, pinned: !item.pinned };44 if (toggled.pinned) updated.push(toggled);45 else updated.unshift(toggled);46 return updated;47 });48 // Reset group z-index after the animation duration (keep in sync with animation timing)49 setTimeout(() => setTogglingGroup(null), zIndexResetDelay);50 };51 52 return (53 <motion.div className={cn('space-y-10', className)} {...props}>54 <LayoutGroup>55 <div>56 {pinned.length > 0 && (57 <div58 className={cn(59 'space-y-3 relative',60 togglingGroup === 'pinned' ? 'z-5' : 'z-10',61 pinnedSectionClassName62 )}>63 {pinned.map((item) => (64 <motion.div65 key={item.id}66 layoutId={`item-${item.id}`}67 onClick={() => toggleStatus(item.id)}68 transition={transition}69 className="flex items-center justify-between gap-5 rounded-2xl bg-neutral-200 dark:bg-neutral-800 p-2">70 <div className="flex items-center gap-2">71 <div className="rounded-lg bg-background p-2">72 <item.icon className="size-5 text-neutral-500 dark:text-neutral-400" />73 </div>74 <div>75 <div className="text-sm font-semibold">{item.name}</div>76 <div className="text-xs text-neutral-500 dark:text-neutral-400 font-medium">77 {item.info}78 </div>79 </div>80 </div>81 <div82 className="flex items-center justify-center size-8 rounded-full bg-neutral-400 dark:bg-neutral-600">83 <Pin className="size-4 text-white fill-white" />84 </div>85 </motion.div>86 ))}87 </div>88 )}89 </div>90 91 <div>92 <AnimatePresence>93 {unpinned.length > 0 && (94 <motion.p95 layout96 key="all-label"97 className={cn(98 'font-medium px-3 text-neutral-500 dark:text-neutral-300 text-sm mb-2',99 labelClassName100 )}101 {...labelMotionProps}>102 {labels.unpinned}103 </motion.p>104 )}105 </AnimatePresence>106 {unpinned.length > 0 && (107 <div108 className={cn(109 'space-y-3 relative',110 togglingGroup === 'unpinned' ? 'z-5' : 'z-10',111 unpinnedSectionClassName112 )}>113 {unpinned.map((item) => (114 <motion.div115 key={item.id}116 layoutId={`item-${item.id}`}117 onClick={() => toggleStatus(item.id)}118 transition={transition}119 className="flex items-center justify-between gap-5 rounded-2xl bg-neutral-200 dark:bg-neutral-800 p-2 group">120 <div className="flex items-center gap-2">121 <div className="rounded-lg bg-background p-2">122 <item.icon className="size-5 text-neutral-500 dark:text-neutral-400" />123 </div>124 <div>125 <div className="text-sm font-semibold">{item.name}</div>126 <div className="text-xs text-neutral-500 dark:text-neutral-400 font-medium">127 {item.info}128 </div>129 </div>130 </div>131 <div132 className="flex items-center justify-center size-8 rounded-full bg-neutral-400 dark:bg-neutral-600 opacity-0 group-hover:opacity-100 transition-opacity duration-250">133 <Pin className="size-4 text-white" />134 </div>135 </motion.div>136 ))}137 </div>138 )}139 </div>140 </LayoutGroup>141 </motion.div>142 );143}144 145export { PinList };146 Uso
Props
PropTipoDefaultDescripción
itemsArray<{id, name, info, icon, pinned}>—Ítems de la lista.
labels{pinned, unpinned}…Etiquetas de sección.
transitionTransitionspringTransición de reordenamiento.
Acciones del componente
Search for a command to run...
Potenciá tu negocio con SFESS
Software a medida, IA integrada y ciberseguridad avanzada para empresas.
0años de ingeniería
0proyectos entregados
0% uptime
Servicios destacados
Consultoría SEO Avanzado & Analítica
Auditoría técnica profunda para posicionar tu negocio en los primeros resultados de Google.
SEO TécnicoGA4Páginas Web Corporativas Complejas
Plataformas empresariales masivas con múltiples secciones y animaciones fluidas.
Framer MotionMulti-pageIntegración de Inteligencia Artificial en Negocios
Automatizamos la atención, las reservas y las ventas de tu negocio con agentes de IA.
AI AgentsChatbots
Nuestro ecosistema
Ver todos los serviciosWORKENTA_OSSFESS_SOLUTIONSPRANALUZ_YOGAREMY_AI