{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "FolderFloat-TS-CSS",
	"title": "FolderFloat",
	"description": "Folder whose notes spring out into a floating cloud when it opens on hover or press.",
	"type": "registry:component",
	"files": [
		{
			"type": "registry:file",
			"path": "FolderFloat.css",
			"target": "@components/FolderFloat.css",
			"content": ".folder-float {\n  --ff-w: 200px;\n  --ff-h: 148px;\n  --ff-r: 14px;\n  --ff-tab: 14px;\n  --ff-back: #3f3f46;\n  --ff-front: #52525b;\n  --ff-paper: #f5f5f5;\n  --ff-item: #f5f5f5;\n  --ff-item-ink: #18181b;\n  --ff-label: #f5f5f5;\n  --ff-spread: 180px;\n  --ff-lift: 26px;\n  --ff-angle: 34deg;\n  --ff-rest: 16deg;\n  --ff-open: 520ms;\n  --ff-close: 312ms;\n  --ff-stagger: 45ms;\n  --ff-n: 6;\n  --ff-spring: cubic-bezier(0.34, 1.57, 0.64, 1);\n  --ff-ease-out: cubic-bezier(0.23, 1, 0.32, 1);\n\n  position: relative;\n  display: inline-block;\n  width: var(--ff-w);\n  padding-top: var(--ff-tab);\n  font-family: inherit;\n  font-size: 13px;\n  font-weight: 500;\n  line-height: 1;\n}\n\n.folder-float__folder {\n  position: relative;\n  width: var(--ff-w);\n  height: var(--ff-h);\n}\n\n.folder-float__back {\n  position: absolute;\n  inset: 0;\n  z-index: 0;\n  border-radius: var(--ff-r);\n  background: var(--ff-back);\n  transform: perspective(600px) rotateX(8deg);\n  transform-origin: 50% 100%;\n}\n\n.folder-float__back::before {\n  content: '';\n  position: absolute;\n  top: calc(-1 * var(--ff-tab));\n  left: 0;\n  width: 42%;\n  height: calc(var(--ff-tab) + var(--ff-r));\n  border-radius: var(--ff-r) var(--ff-r) 0 0;\n  background: inherit;\n}\n\n.folder-float__paper {\n  position: absolute;\n  top: 10%;\n  z-index: 1;\n  right: 8%;\n  left: 8%;\n  height: 50%;\n  border-radius: 6px;\n  background: var(--ff-paper);\n  opacity: 0;\n  transform: translateY(10px);\n  transition:\n    transform var(--ff-close) var(--ff-ease-out),\n    opacity var(--ff-close) ease;\n}\n\n.folder-float__front {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 2;\n  display: flex;\n  flex-direction: column;\n  justify-content: flex-end;\n  gap: 5px;\n  height: 76%;\n  padding: 14px 16px;\n  box-sizing: border-box;\n  border-radius: var(--ff-r);\n  background: linear-gradient(180deg, color-mix(in srgb, var(--ff-front) 92%, #fff), var(--ff-front) 60%);\n  color: var(--ff-label);\n  box-shadow: 0 -10px 24px rgba(0, 0, 0, 0.28);\n  transform: perspective(600px) rotateX(calc(-1 * var(--ff-rest)));\n  transform-origin: 50% 100%;\n  transition: transform var(--ff-open) var(--ff-ease-out);\n}\n\n.folder-float__label {\n  font-size: 13px;\n  font-weight: 500;\n}\n\n.folder-float__sub {\n  font-size: 11px;\n  opacity: 0.55;\n}\n\n.folder-float__trigger {\n  position: absolute;\n  right: 0;\n  bottom: 0;\n  left: 0;\n  z-index: 3;\n  height: 76%;\n  margin: 0;\n  padding: 0;\n  border: 0;\n  border-radius: var(--ff-r);\n  background: transparent;\n  cursor: pointer;\n  outline: none;\n  -webkit-tap-highlight-color: transparent;\n}\n\n.folder-float[data-open] .folder-float__front {\n  transform: perspective(600px) rotateX(calc(-1 * var(--ff-angle)));\n}\n\n.folder-float[data-open] .folder-float__paper {\n  opacity: 1;\n  transform: translateY(0);\n  transition:\n    transform var(--ff-open) var(--ff-ease-out),\n    opacity 200ms ease;\n}\n\n.folder-float__items {\n  position: absolute;\n  top: var(--ff-tab);\n  left: 50%;\n  z-index: 1;\n  width: 0;\n  height: 0;\n}\n\n.folder-float[data-open] .folder-float__items::before {\n  content: '';\n  position: absolute;\n  top: calc(-1 * (var(--ff-lift) + 120px));\n  left: calc(-1 * (var(--ff-spread) + 100px));\n  width: calc(2 * var(--ff-spread) + 200px);\n  height: calc(var(--ff-lift) + 120px);\n}\n\n.folder-float__item {\n  position: absolute;\n  top: 0;\n  left: 50%;\n  margin: 0;\n  padding: 0 14px;\n  height: 34px;\n  border: 0;\n  border-radius: 17px;\n  background: var(--ff-item);\n  color: var(--ff-item-ink);\n  font: inherit;\n  white-space: nowrap;\n  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14);\n  cursor: pointer;\n  outline: none;\n  opacity: 0;\n  transform: translate(-50%, 44px) scale(0.6);\n  transform-origin: 50% 50%;\n  pointer-events: none;\n  -webkit-tap-highlight-color: transparent;\n  transition:\n    transform var(--ff-close) var(--ff-ease-out) calc((var(--ff-n) - 1 - var(--i)) * var(--ff-stagger) * 0.5),\n    opacity 160ms ease calc((var(--ff-n) - 1 - var(--i)) * var(--ff-stagger) * 0.5 + var(--ff-close) * 0.45),\n    scale 160ms var(--ff-ease-out);\n}\n\n.folder-float[data-open] .folder-float__item {\n  opacity: 1;\n  transform: translate(calc(-50% + var(--x)), var(--y)) rotate(var(--r)) scale(1);\n  pointer-events: auto;\n  transition:\n    transform var(--ff-open) var(--ff-spring) calc(var(--i) * var(--ff-stagger)),\n    opacity 160ms ease calc(var(--i) * var(--ff-stagger)),\n    scale 160ms var(--ff-ease-out);\n}\n\n.folder-float[data-live] .folder-float__item {\n  cursor: grab;\n  transition: scale 160ms var(--ff-ease-out);\n}\n\n.folder-float__item[data-drag] {\n  cursor: grabbing;\n}\n\n@media (hover: hover) and (pointer: fine) {\n  .folder-float[data-open] .folder-float__item:hover {\n    scale: 1.05;\n  }\n}\n\n.folder-float[data-open] .folder-float__item:active {\n  scale: 0.97;\n}\n\n.folder-float__item[data-pop] {\n  animation: folder-float-pop 320ms var(--ff-ease-out);\n}\n\n.folder-float__drift {\n  display: block;\n  animation: folder-float-drift 3.2s ease-in-out infinite;\n  animation-delay: calc(var(--i) * -0.7s);\n  animation-play-state: paused;\n}\n\n.folder-float[data-open] .folder-float__drift {\n  animation-play-state: running;\n}\n\n.folder-float[data-physics] .folder-float__drift,\n.folder-float[data-live] .folder-float__drift {\n  animation: none;\n}\n\n@keyframes folder-float-drift {\n  0%,\n  100% {\n    translate: 0 0;\n  }\n\n  50% {\n    translate: 0 -3px;\n  }\n}\n\n@keyframes folder-float-pop {\n  30% {\n    scale: 1.1;\n  }\n\n  100% {\n    scale: 1;\n  }\n}\n\n@media (prefers-reduced-motion: reduce) {\n  .folder-float__front,\n  .folder-float__paper {\n    transition: opacity 200ms ease;\n  }\n\n  .folder-float[data-open] .folder-float__front {\n    transform: perspective(600px) rotateX(calc(-1 * var(--ff-rest)));\n  }\n\n  .folder-float__paper {\n    transform: none !important;\n  }\n\n  .folder-float__item {\n    transition: opacity 200ms ease;\n  }\n\n  .folder-float[data-open] .folder-float__item {\n    transition: opacity 200ms ease calc(var(--i) * var(--ff-stagger));\n  }\n\n  .folder-float__drift {\n    animation: none;\n  }\n}\n"
		},
		{
			"type": "registry:component",
			"path": "FolderFloat.tsx",
			"content": "'use client';\n\nimport React, {\n  useCallback,\n  useEffect,\n  useLayoutEffect,\n  useRef,\n  useState,\n  type CSSProperties,\n  type PointerEvent\n} from 'react';\nimport Matter from 'matter-js';\nimport './FolderFloat.css';\n\nconst { Bodies, Body, Composite, Engine } = Matter;\n\nexport type FolderFloatItem = string | { label: string; value: string };\nexport type FolderFloatTrigger = 'hover' | 'click';\n\nexport interface FolderFloatProps {\n  items?: FolderFloatItem[];\n  label?: string;\n  sublabel?: string;\n  trigger?: FolderFloatTrigger;\n  defaultOpen?: boolean;\n  closeOnSelect?: boolean;\n  physics?: boolean;\n  drift?: number;\n  onSelect?: (value: string, index: number) => void;\n  onOpenChange?: (open: boolean) => void;\n  folderColor?: string;\n  frontColor?: string;\n  paperColor?: string;\n  itemColor?: string;\n  itemTextColor?: string;\n  labelColor?: string;\n  width?: number;\n  height?: number;\n  radius?: number;\n  spread?: number;\n  lift?: number;\n  tilt?: number;\n  flapAngle?: number;\n  restAngle?: number;\n  openDuration?: number;\n  stagger?: number;\n  bounce?: number;\n  className?: string;\n}\n\ntype Entry = { label: string; value: string };\ntype Size = { w: number; h: number };\ntype Zone = { left: number; right: number; top: number; bottom: number };\ntype Drag = { i: number; id: number; dx: number; dy: number; sx: number; sy: number; moved: boolean };\ninterface World {\n  engine: Matter.Engine | null;\n  bodies: Matter.Body[];\n  sizes: Size[];\n  raf: number;\n  last: number;\n  t0: number;\n  drag: Drag | null;\n  zone: Zone | null;\n  live: boolean;\n}\ninterface Latest {\n  onSelect?: FolderFloatProps['onSelect'];\n  onOpenChange?: FolderFloatProps['onOpenChange'];\n  drift: number;\n  reduce: boolean;\n}\n\nconst DEFAULT_ITEMS: FolderFloatItem[] = [\n  'Try a warmer palette',\n  'Tighten the spacing',\n  'Logo feels small',\n  'Love the new hero'\n];\nconst PAD = 28;\nconst CHAR = 6.8;\nconst GAP = 12;\nconst ROW = 52;\nconst DRAG_MIN = 4;\nconst ZONE_PAD = 8;\n\nconst jitter = (i: number) => {\n  const x = Math.sin(i * 12.9898 + 4.1414) * 43758.5453;\n  return x - Math.floor(x);\n};\n\nconst layout = (list: Entry[], spread: number, lift: number, tilt: number, sizes: (Size | null)[]) => {\n  const rows: { items: { i: number; pw: number }[]; width: number }[] = [];\n  let row: { i: number; pw: number }[] = [];\n  let width = 0;\n  list.forEach((item, i) => {\n    const pw = sizes[i]?.w ?? PAD + item.label.length * CHAR;\n    if (row.length && width + GAP + pw > spread * 2) {\n      rows.push({ items: row, width });\n      row = [];\n      width = 0;\n    }\n    row.push({ i, pw });\n    width += (row.length > 1 ? GAP : 0) + pw;\n  });\n  if (row.length) rows.push({ items: row, width });\n  const pos: { x: number; y: number; r: number }[] = [];\n  rows.forEach((r, ri) => {\n    let x = -r.width / 2;\n    const shift = (ri % 2 ? 1 : -1) * Math.min(16, spread * 0.1);\n    r.items.forEach(({ i, pw }) => {\n      const j = jitter(i);\n      pos[i] = { x: x + pw / 2 + shift + (j - 0.5) * 6, y: -lift - ri * ROW - j * 6, r: tilt * (j * 2 - 1) };\n      x += pw + GAP;\n    });\n  });\n  return pos;\n};\n\nconst FolderFloat: React.FC<FolderFloatProps> = ({\n  items = DEFAULT_ITEMS,\n  label = 'Design feedback',\n  sublabel = '',\n  trigger = 'hover',\n  defaultOpen = false,\n  closeOnSelect = true,\n  physics = true,\n  drift = 0.5,\n  onSelect,\n  onOpenChange,\n  folderColor = '#3f3f46',\n  frontColor = '#52525b',\n  paperColor = '#f5f5f5',\n  itemColor = '#f5f5f5',\n  itemTextColor = '#18181b',\n  labelColor = '#f5f5f5',\n  width = 200,\n  height = 148,\n  radius = 14,\n  spread = 180,\n  lift = 26,\n  tilt = 8,\n  flapAngle = 34,\n  restAngle = 16,\n  openDuration = 520,\n  stagger = 45,\n  bounce = 0.3,\n  className = ''\n}) => {\n  const [open, setOpen] = useState(defaultOpen);\n  const [popped, setPopped] = useState(-1);\n  const [live, setLive] = useState(false);\n  const [sizes, setSizes] = useState<Size[]>([]);\n  const anchorRef = useRef<HTMLDivElement>(null);\n  const pillRefs = useRef<(HTMLButtonElement | null)[]>([]);\n  const world = useRef<World>({\n    engine: null,\n    bodies: [],\n    sizes: [],\n    raf: 0,\n    last: 0,\n    t0: 0,\n    drag: null,\n    zone: null,\n    live: false\n  });\n  const latest = useRef<Latest>({} as Latest);\n  latest.current = { onSelect, onOpenChange, drift, reduce: false };\n  const popTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  const liveTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n  const list: Entry[] = items.map(item => (typeof item === 'string' ? { label: item, value: item } : item));\n  const n = list.length;\n  const sub = sublabel || `${n} ${n === 1 ? 'note' : 'notes'}`;\n  const pos = layout(list, spread, lift, tilt, sizes);\n\n  const labelsKey = list.map(item => item.label).join('|');\n  useLayoutEffect(() => {\n    const measure = () => {\n      const next = pillRefs.current.slice(0, n).map(el => (el ? { w: el.offsetWidth, h: el.offsetHeight } : null));\n      if (next.some(s => !s)) return;\n      const sized = next as Size[];\n      setSizes(prev =>\n        prev.length === sized.length && prev.every((s, i) => s.w === sized[i].w && s.h === sized[i].h) ? prev : sized\n      );\n    };\n    measure();\n    document.fonts?.ready.then(measure);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [n, labelsKey]);\n\n  const stopPhysics = useCallback(() => {\n    const w = world.current;\n    clearTimeout(liveTimer.current);\n    cancelAnimationFrame(w.raf);\n    w.raf = 0;\n    if (w.engine) {\n      w.bodies.forEach((b, i) => {\n        const el = pillRefs.current[i];\n        if (!el) return;\n        el.style.setProperty('--x', `${b.position.x.toFixed(1)}px`);\n        el.style.setProperty('--y', `${(b.position.y - w.sizes[i].h / 2).toFixed(1)}px`);\n      });\n      Composite.clear(w.engine.world, false, true);\n      Engine.clear(w.engine);\n      w.engine = null;\n    }\n    w.bodies = [];\n    w.drag = null;\n    w.live = false;\n    setLive(false);\n  }, []);\n\n  const startPhysics = useCallback(() => {\n    const w = world.current;\n    if (w.engine) return;\n    const els = pillRefs.current.slice(0, n);\n    if (els.some(el => !el)) return;\n    const engine = Engine.create({ gravity: { x: 0, y: 0 } });\n    engine.enableSleeping = false;\n    w.engine = engine;\n    w.sizes = (els as HTMLButtonElement[]).map(el => ({ w: el.offsetWidth, h: el.offsetHeight }));\n    const ys = pos.map(p => p.y);\n    const zone = {\n      left: -spread - ZONE_PAD,\n      right: spread + ZONE_PAD,\n      top: Math.min(...ys) - ZONE_PAD,\n      bottom: -lift + Math.max(...w.sizes.map(s => s.h))\n    };\n    w.zone = zone;\n    w.bodies = els.map((el, i) => {\n      const { w: bw, h: bh } = w.sizes[i];\n      const b = Bodies.rectangle(pos[i].x, pos[i].y + bh / 2, bw, bh, {\n        chamfer: { radius: Math.min(bh / 2 - 1, 16) },\n        restitution: 0.55,\n        friction: 0,\n        frictionAir: 0.08,\n        inertia: Infinity\n      });\n      (b as Matter.Body & { plugin: { phase: number } }).plugin = { phase: jitter(i) * Math.PI * 2 };\n      return b;\n    });\n    const T = 80;\n    const walls = [\n      Bodies.rectangle((zone.left + zone.right) / 2, zone.top - T / 2, zone.right - zone.left + 2 * T, T, {\n        isStatic: true\n      }),\n      Bodies.rectangle((zone.left + zone.right) / 2, zone.bottom + T / 2, zone.right - zone.left + 2 * T, T, {\n        isStatic: true\n      }),\n      Bodies.rectangle(zone.left - T / 2, (zone.top + zone.bottom) / 2, T, zone.bottom - zone.top + 2 * T, {\n        isStatic: true\n      }),\n      Bodies.rectangle(zone.right + T / 2, (zone.top + zone.bottom) / 2, T, zone.bottom - zone.top + 2 * T, {\n        isStatic: true\n      })\n    ];\n    Composite.add(engine.world, [...w.bodies, ...walls]);\n    w.live = true;\n    w.last = 0;\n    w.t0 = performance.now();\n    setLive(true);\n    const tick = (now: number) => {\n      const s = world.current;\n      if (!s.engine) return;\n      const dt = s.last ? Math.min(32, now - s.last) : 16;\n      s.last = now;\n      const t = (now - s.t0) / 1000;\n      const k = latest.current.drift * 0.00005 * Math.min(1, t / 2);\n      s.bodies.forEach((b, i) => {\n        if (s.drag && s.drag.i === i) return;\n        const ph = (b as Matter.Body & { plugin: { phase: number } }).plugin.phase;\n        Body.applyForce(b, b.position, {\n          x: Math.sin(t * 0.9 + ph) * k * b.mass,\n          y: Math.cos(t * 1.3 + ph * 1.7) * k * b.mass\n        });\n      });\n      Engine.update(s.engine, dt);\n      s.bodies.forEach((b, i) => {\n        const el = pillRefs.current[i];\n        if (!el) return;\n        el.style.setProperty('--x', `${b.position.x.toFixed(1)}px`);\n        el.style.setProperty('--y', `${(b.position.y - s.sizes[i].h / 2).toFixed(1)}px`);\n      });\n      s.raf = requestAnimationFrame(tick);\n    };\n    w.raf = requestAnimationFrame(tick);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [n, spread, lift, pos.map(p => `${p.x},${p.y}`).join('|')]);\n\n  const set = useCallback(\n    (next: boolean) => {\n      if (!next) stopPhysics();\n      setOpen(prev => {\n        if (prev === next) return prev;\n        latest.current.onOpenChange?.(next);\n        return next;\n      });\n    },\n    [stopPhysics]\n  );\n\n  useEffect(() => {\n    clearTimeout(liveTimer.current);\n    if (!open || !physics || latest.current.reduce) {\n      if (!open) stopPhysics();\n      else if (!physics) stopPhysics();\n      return undefined;\n    }\n    liveTimer.current = setTimeout(startPhysics, openDuration + (n - 1) * stagger + 80);\n    return () => clearTimeout(liveTimer.current);\n  }, [open, physics, openDuration, stagger, n, startPhysics, stopPhysics]);\n\n  useEffect(() => {\n    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');\n    const sync = () => {\n      latest.current.reduce = mq.matches;\n    };\n    sync();\n    mq.addEventListener('change', sync);\n    return () => mq.removeEventListener('change', sync);\n  }, []);\n\n  useEffect(\n    () => () => {\n      clearTimeout(popTimer.current);\n      stopPhysics();\n    },\n    [stopPhysics]\n  );\n\n  const pick = (item: Entry, i: number) => {\n    latest.current.onSelect?.(item.value, i);\n    clearTimeout(popTimer.current);\n    setPopped(i);\n    popTimer.current = setTimeout(() => setPopped(-1), 320);\n    if (closeOnSelect) set(false);\n  };\n\n  const pointerAt = (e: PointerEvent<HTMLButtonElement>) => {\n    const r = anchorRef.current?.getBoundingClientRect();\n    return r ? { x: e.clientX - r.left, y: e.clientY - r.top } : { x: 0, y: 0 };\n  };\n  const down = (e: PointerEvent<HTMLButtonElement>, i: number) => {\n    const w = world.current;\n    if (!w.live || e.button !== 0) return;\n    const b = w.bodies[i];\n    if (!b) return;\n    const p = pointerAt(e);\n    w.drag = {\n      i,\n      id: e.pointerId,\n      dx: b.position.x - p.x,\n      dy: b.position.y - p.y,\n      sx: e.clientX,\n      sy: e.clientY,\n      moved: false\n    };\n    try {\n      e.currentTarget.setPointerCapture(e.pointerId);\n    } catch {}\n  };\n  const move = (e: PointerEvent<HTMLButtonElement>, i: number) => {\n    const w = world.current;\n    const d = w.drag;\n    if (!d || d.i !== i || d.id !== e.pointerId) return;\n    if (!d.moved && Math.hypot(e.clientX - d.sx, e.clientY - d.sy) >= DRAG_MIN) {\n      d.moved = true;\n      e.currentTarget.setAttribute('data-drag', '');\n    }\n    if (!d.moved) return;\n    const b = w.bodies[i];\n    const { w: bw, h: bh } = w.sizes[i];\n    const z = w.zone as Zone;\n    const p = pointerAt(e);\n    const x = Math.min(z.right - bw / 2, Math.max(z.left + bw / 2, p.x + d.dx));\n    const y = Math.min(z.bottom - bh / 2, Math.max(z.top + bh / 2, p.y + d.dy));\n    Body.setVelocity(b, { x: (x - b.position.x) * 0.6, y: (y - b.position.y) * 0.6 });\n    Body.setPosition(b, { x, y });\n  };\n  const up = (e: PointerEvent<HTMLButtonElement>, i: number, item: Entry) => {\n    const w = world.current;\n    const d = w.drag;\n    if (!d || d.i !== i || d.id !== e.pointerId) return;\n    w.drag = null;\n    e.currentTarget.removeAttribute('data-drag');\n    try {\n      e.currentTarget.releasePointerCapture(e.pointerId);\n    } catch {}\n    if (!d.moved && e.type === 'pointerup') pick(item, i);\n  };\n\n  const hover = trigger === 'hover';\n\n  return (\n    <div\n      className={`folder-float${className ? ` ${className}` : ''}`}\n      data-open={open ? '' : undefined}\n      data-live={live ? '' : undefined}\n      data-physics={physics ? '' : undefined}\n      data-trigger={trigger}\n      onPointerEnter={hover ? () => set(true) : undefined}\n      onPointerLeave={\n        hover\n          ? () => {\n              if (!world.current.drag) set(false);\n            }\n          : undefined\n      }\n      onKeyDown={(e: React.KeyboardEvent<HTMLDivElement>) => {\n        if (e.key === 'Escape' && open) {\n          e.stopPropagation();\n          set(false);\n        }\n      }}\n      style={\n        {\n          '--ff-w': `${width}px`,\n          '--ff-h': `${height}px`,\n          '--ff-r': `${radius}px`,\n          '--ff-back': folderColor,\n          '--ff-front': frontColor,\n          '--ff-paper': paperColor,\n          '--ff-item': itemColor,\n          '--ff-item-ink': itemTextColor,\n          '--ff-label': labelColor,\n          '--ff-spread': `${spread}px`,\n          '--ff-lift': `${lift}px`,\n          '--ff-angle': `${flapAngle}deg`,\n          '--ff-rest': `${restAngle}deg`,\n          '--ff-open': `${openDuration}ms`,\n          '--ff-close': `${Math.round(openDuration * 0.6)}ms`,\n          '--ff-stagger': `${stagger}ms`,\n          '--ff-n': n,\n          '--ff-spring': `cubic-bezier(0.34, ${(1 + bounce * 1.9).toFixed(2)}, 0.64, 1)`\n        } as CSSProperties\n      }\n    >\n      <div ref={anchorRef} className=\"folder-float__items\">\n        {list.map((item, i) => {\n          const p = pos[i];\n          return (\n            <button\n              key={`${item.value}-${i}`}\n              ref={el => {\n                pillRefs.current[i] = el;\n              }}\n              type=\"button\"\n              className=\"folder-float__item\"\n              tabIndex={open ? 0 : -1}\n              aria-hidden={!open}\n              data-pop={popped === i ? '' : undefined}\n              style={\n                {\n                  '--i': i,\n                  '--x': `${p.x.toFixed(1)}px`,\n                  '--y': `${p.y.toFixed(1)}px`,\n                  '--r': `${p.r.toFixed(2)}deg`\n                } as CSSProperties\n              }\n              onPointerDown={e => down(e, i)}\n              onPointerMove={e => move(e, i)}\n              onPointerUp={e => up(e, i, item)}\n              onPointerCancel={e => up(e, i, item)}\n              onClick={e => {\n                if (!world.current.live || e.detail === 0) pick(item, i);\n              }}\n            >\n              <span className=\"folder-float__drift\">{item.label}</span>\n            </button>\n          );\n        })}\n      </div>\n      <div className=\"folder-float__folder\">\n        <span className=\"folder-float__back\" aria-hidden=\"true\" />\n        <span className=\"folder-float__paper\" aria-hidden=\"true\" />\n        <span className=\"folder-float__front\" aria-hidden=\"true\">\n          <span className=\"folder-float__label\">{label}</span>\n          <span className=\"folder-float__sub\">{sub}</span>\n        </span>\n        <button\n          type=\"button\"\n          className=\"folder-float__trigger\"\n          aria-expanded={open}\n          aria-label={`${label}, ${sub}`}\n          onClick={() => set(!open)}\n        />\n      </div>\n    </div>\n  );\n};\n\nexport default FolderFloat;\n"
		}
	],
	"registryDependencies": [],
	"dependencies": [
		"matter-js@^0.20.0"
	],
	"devDependencies": [
		"@types/matter-js@^0.19.8"
	]
}