npm package: @picpin/core
Description: Headless image viewer with zoom, pan and pin support for React
Author: Anderson Rosa
Latest version: 1.0.2

# @picpin/core

<p align="center">
  <strong>🖼️ Headless image viewer with zoom, pan and pin support for React</strong>
</p>

<p align="center">
  <a href="https://www.npmjs.com/package/@picpin/core">
    <img src="https://img.shields.io/npm/v/@picpin/core?style=flat-square" alt="npm version" />
  </a>
  <a href="https://www.npmjs.com/package/@picpin/core">
    <img src="https://img.shields.io/npm/dm/@picpin/core?style=flat-square" alt="npm downloads" />
  </a>
  <a href="https://bundlephobia.com/package/@picpin/core">
    <img src="https://img.shields.io/bundlephobia/minzip/@picpin/core?style=flat-square" alt="bundle size" />
  </a>
  <a href="https://github.com/yourusername/picpin/blob/main/LICENSE">
    <img src="https://img.shields.io/npm/l/@picpin/core?style=flat-square" alt="license" />
  </a>
</p>

## What is PicPin?

PicPin (Picture Pin) is a **headless React library** that provides all the logic needed to create interactive image viewers with:

- 🔍 **Zoom** - Mouse wheel, touch gestures, and programmatic controls
- 🖐️ **Pan** - Drag to move images when zoomed
- 📍 **Pins** - Add markers/annotations at specific coordinates
- 🎯 **Coordinates** - Automatic conversion between image, viewport, and container coordinates
- 🎨 **Headless** - No forced styles, you have complete control over the UI
- ⚡ **Performant** - Optimized calculations and minimal re-renders
- 📦 **Lightweight** - ~15kb gzipped with only one dependency (zustand)
- 🔧 **TypeScript** - Fully typed for excellent DX

## Installation

```bash
npm install @picpin/core
# or
yarn add @picpin/core
# or
pnpm add @picpin/core
```

## Quick Start

```tsx
import {
  PicViewerProvider,
  ViewerContainer,
  ViewerImage,
  PinLayer,
  usePicPin,
  useViewPort,
  useZoom,
  useDynamicPins
} from "@picpin/core";

function App() {
  // Create a client service (your custom logic)
  const clientService = (context) => ({
    onPinClick: (pin) => console.log("Pin clicked:", pin)
  });

  return (
    <PicViewerProvider picPinClientService={clientService}>
      <ImageViewer />
    </PicViewerProvider>
  );
}

function ImageViewer() {
  const { handlers, containerRef, imageRef, controller } = usePicPin();
  const viewport = useViewPort();
  const zoom = useZoom();
  const dynamicPins = useDynamicPins();

  // Load an image
  React.useEffect(() => {
    controller.loadFile({
      url: "https://picsum.photos/800/600",
      name: "sample.jpg"
    });
  }, []);

  // Add pin on click
  const handleClick = (e) => {
    const rect = containerRef.current?.getBoundingClientRect();
    if (!rect) return;
    
    const coords = controller.getImageCoordinates(
      e.clientX - rect.left,
      e.clientY - rect.top
    );
    
    if (coords) {
      controller.addPin(coords.x, coords.y);
    }
  };

  return (
    <ViewerContainer
      containerRef={containerRef}
      handlers={{
        ...handlers.containerProps(),
        onClick: handleClick
      }}
      style={{ width: "100%", height: "500px", position: "relative" }}
    >
      <ViewerImage
        imageRef={imageRef}
        src="https://picsum.photos/800/600"
        viewport={viewport}
        zoom={zoom}
      />
      
      <PinLayer viewport={viewport}>
        {dynamicPins.map(({ pin, style }) => (
          <div key={pin.id} style={style}>
            📍
          </div>
        ))}
      </PinLayer>
    </ViewerContainer>
  );
}
```

## Use Cases

PicPin is perfect for building:

- 🏠 **Virtual Tours** - Navigate between rooms with hotspot navigation
- 🎨 **Color Pickers** - Pick multiple colors from images
- 📝 **Image Annotators** - Add comments and notes to specific areas
- 🗺️ **Interactive Maps** - Navigate large images with markers
- 🏥 **Medical Viewers** - Mark points of interest in medical images
- 🛍️ **Product Showcases** - Highlight product features
- 📐 **Measurement Tools** - Measure distances and areas
- 🎮 **Game Maps** - Interactive game world maps

## Core Concepts

### 1. **Headless Architecture**
PicPin provides the logic, you provide the UI. This means:
- Complete control over styling
- Use any UI library (Material-UI, Ant Design, Tailwind, etc.)
- No CSS conflicts
- Smaller bundle size

### 2. **Coordinate Systems**
PicPin handles three coordinate systems automatically:
- **Image coordinates** - Relative to the original image
- **Viewport coordinates** - Relative to the visible area
- **Container coordinates** - Relative to the DOM container

### 3. **Pin Agnostic**
Pins are just data with positions. You decide:
- What they look like
- What they do when clicked
- What metadata they carry
- How they behave

## API Overview

### Hooks

```tsx
// Main hook - access everything
const { controller, handlers, containerRef, imageRef } = usePicPin();

// State hooks with selectors
const pins = usePicPinState(state => state.pins);
const viewport = useViewPort();
const zoom = useZoom();

// Pins with calculated styles
const dynamicPins = useDynamicPins();

// Keyboard shortcuts
useKeyboardShortcuts({
  onZoomIn: () => controller.zoomIn(),
  onZoomOut: () => controller.zoomOut(),
  onDelete: () => controller.deleteSelectedPin()
});
```

### Controller API

```tsx
// File operations
controller.loadFile({ url, name });
controller.clearFile();

// Pin operations
controller.addPin(x, y, metadata);
controller.removePin(pinId);
controller.selectPin(pinId);
controller.setPins(pins);

// View operations
controller.zoomIn();
controller.zoomOut();
controller.zoomTo(level);
controller.fitToView();
controller.resetView();

// Coordinate conversion
controller.getImageCoordinates(containerX, containerY);
controller.getContainerCoordinates(imageX, imageY);

// Import/Export
controller.exportView();
controller.importView(viewData);
```

### Components

All components are headless (no styles):

```tsx
<ViewerContainer>      {/* Main container */}
<ViewerImage>         {/* Image with transforms */}
<PinLayer>           {/* Pin container with viewport offset */}
<PinWrapper>         {/* Individual pin wrapper */}
<DefaultPin>         {/* Example pin component */}
```

## Advanced Examples

### Photo Navigator with Hotspots

```tsx
const photos = [
  {
    id: "room1",
    url: "/room1.jpg",
    hotspots: [
      { position: { x: 400, y: 300 }, targetRoom: "room2" }
    ]
  }
];

function PhotoNavigator() {
  const [currentRoom, setCurrentRoom] = useState("room1");
  
  const clientService = (context) => ({
    onPinClick: (pin) => {
      setCurrentRoom(pin.metadata.targetRoom);
    }
  });
  
  // ... render logic
}
```

### Multi-point Color Picker

```tsx
function ColorPicker() {
  const [colors, setColors] = useState([]);
  
  const clientService = (context) => ({
    onPinAdd: async (pin) => {
      const color = await extractColorAtPoint(
        context.imageRef.current,
        pin.position
      );
      setColors([...colors, { id: pin.id, color }]);
    }
  });
  
  // ... render logic
}
```

## Features

- ✅ **Touch Support** - Works on mobile devices
- ✅ **Keyboard Shortcuts** - Customizable keyboard controls  
- ✅ **Events System** - React to viewer changes
- ✅ **Constraints** - Limit zoom and pan ranges
- ✅ **High DPI Support** - Sharp images on retina displays
- ✅ **Server-Side Rendering** - SSR compatible
- ✅ **Accessibility** - Keyboard navigation support

## Browser Support

- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- iOS Safari (latest)
- Chrome Android (latest)

## TypeScript

PicPin is written in TypeScript and provides comprehensive type definitions:

```typescript
import { Pin, ViewportState, PicViewerController } from "@picpin/core";

interface MyPinMetadata {
  label: string;
  color: string;
  category: "hotspot" | "annotation" | "measurement";
}

const pin: Pin<MyPinMetadata> = {
  id: "pin-1",
  position: { x: 100, y: 200 },
  metadata: {
    label: "Room entrance",
    color: "#ff0000",
    category: "hotspot"
  },
  createdAt: new Date(),
  updatedAt: new Date()
};
```

## Performance

PicPin is optimized for performance:

- **Efficient State Management** - Uses Zustand with selectors
- **Minimal Re-renders** - Components only update when needed
- **CSS Transforms** - Hardware-accelerated positioning
- **Event Delegation** - Single event listener for all pins
- **Memoized Calculations** - Complex calculations are cached

## Documentation

Full documentation is available at [GitHub](https://github.com/yourusername/picpin/tree/main/docs):

- [Getting Started Guide](https://github.com/yourusername/picpin/blob/main/docs/getting-started.md)
- [API Reference](https://github.com/yourusername/picpin/blob/main/docs/api-reference.md)
- [Examples](https://github.com/yourusername/picpin/blob/main/docs/examples.md)
- [Advanced Use Cases](https://github.com/yourusername/picpin/blob/main/docs/advanced-use-cases.md)
- [Architecture](https://github.com/yourusername/picpin/blob/main/docs/architecture.md)

## Contributing

Contributions are welcome! Please read our [Contributing Guide](https://github.com/yourusername/picpin/blob/main/CONTRIBUTING.md) for details.

## License

MIT © [Your Name]

---

<p align="center">
  Made with ❤️ by developers, for developers
</p>