{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "use-long-press",
  "title": "Long press",
  "description": "Mouse and touch long-press handlers with start, finish, cancellation, and timer cleanup.",
  "files": [
    {
      "path": "registry/familis/ui/hooks/use-long-press.ts",
      "content": "\"use client\"\n\nimport { useEffect, useMemo, useRef, type MouseEvent, type TouchEvent } from \"react\"\n\nexport type PressEvent = MouseEvent | TouchEvent\n\nexport interface LongPressOptions {\n  threshold?: number\n  onStart?: (event: PressEvent) => void\n  onFinish?: (event: PressEvent) => void\n  onCancel?: (event: PressEvent) => void\n}\n\nfunction isTouchEvent(event: PressEvent): event is TouchEvent {\n  return \"touches\" in event.nativeEvent\n}\n\nfunction isMouseEvent(event: PressEvent): event is MouseEvent {\n  return \"button\" in event.nativeEvent\n}\n\nexport function useLongPress(\n  callback: (event: PressEvent) => void,\n  { threshold = 400, onStart, onFinish, onCancel }: LongPressOptions = {},\n) {\n  const isLongPressActive = useRef(false)\n  const isPressed = useRef(false)\n  const timerId = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n  const callbacks = useRef({ callback, onStart, onFinish, onCancel })\n\n  useEffect(() => {\n    callbacks.current = { callback, onStart, onFinish, onCancel }\n  }, [callback, onStart, onFinish, onCancel])\n\n  useEffect(() => {\n    return () => {\n      clearTimeout(timerId.current)\n      timerId.current = undefined\n      isLongPressActive.current = false\n      isPressed.current = false\n    }\n  }, [threshold])\n\n  return useMemo(() => {\n    const start = (event: PressEvent) => {\n      if (isPressed.current) return\n      if (isMouseEvent(event)) {\n        if (event.button !== 0) return\n      } else if (isTouchEvent(event)) {\n        if (event.touches.length !== 1) return\n      } else {\n        return\n      }\n\n      isPressed.current = true\n      isLongPressActive.current = false\n      timerId.current = setTimeout(() => {\n        timerId.current = undefined\n        isLongPressActive.current = true\n        callbacks.current.callback(event)\n      }, threshold)\n      callbacks.current.onStart?.(event)\n    }\n\n    const cancel = (event: PressEvent) => {\n      if (!isPressed.current) return\n      const finished = isLongPressActive.current\n      clearTimeout(timerId.current)\n      timerId.current = undefined\n      isLongPressActive.current = false\n      isPressed.current = false\n\n      if (finished) {\n        callbacks.current.onFinish?.(event)\n      } else {\n        callbacks.current.onCancel?.(event)\n      }\n    }\n\n    return {\n      onMouseDown: start,\n      onMouseUp: cancel,\n      onMouseLeave: cancel,\n      onTouchStart: start,\n      onTouchEnd: cancel,\n      onTouchCancel: cancel,\n    }\n  }, [threshold])\n}\n",
      "type": "registry:hook"
    }
  ],
  "meta": {
    "storybookId": "ui-hooks-long-press--default"
  },
  "type": "registry:hook"
}