npm package: @orchard9ai/cropper
Description: A React image cropping component with aspect ratio controls
Author: Orchard9 Team
Latest version: 0.8.0

# @orchard9ai/cropper

A React image cropping component with aspect ratio controls, built on top of `react-image-crop`.

## Features

- 🖼️ Image cropping with visual selection
- 📐 Multiple aspect ratio presets (Freeform, Square, Landscape, Portrait, Original)
- 👁️ Optional preview panel
- 🌍 Internationalization support with customizable labels
- 🎨 Dark theme with Tailwind CSS
- ⌨️ Keyboard support (ESC to close)
- 📱 Responsive design
- 🔄 Loading states
- 💾 State management with Zustand

## Installation

```bash
npm install @orchard9ai/cropper react-image-crop
# or
yarn add @orchard9ai/cropper react-image-crop
# or
pnpm add @orchard9ai/cropper react-image-crop
```

**Note**: `react-image-crop` is now a peer dependency and must be installed separately for SSR compatibility.

## Peer Dependencies

This package requires the following peer dependencies:

```json
{
  "react": "^18.0.0",
  "react-dom": "^18.0.0",
  "react-image-crop": "^11.0.0"
}
```

## Usage

### Basic Example

```tsx
import { CropModal, CropData } from '@orchard9ai/cropper';
import '@orchard9ai/cropper/styles'; // Import CSS styles

function App() {
  const [showCropper, setShowCropper] = useState(false);

  const handleCrop = async (cropData: CropData) => {
    console.log('Crop data:', cropData);
    // Send crop data to your API
    await api.cropImage(imageId, cropData);
  };

  return (
    <>
      <button onClick={() => setShowCropper(true)}>Open Cropper</button>

      {showCropper && (
        <CropModal
          imageUrl="https://example.com/image.jpg"
          onClose={() => setShowCropper(false)}
          onCrop={handleCrop}
        />
      )}
    </>
  );
}
```

### With Preview

```tsx
<CropModal
  imageUrl="https://example.com/image.jpg"
  previewUrl="https://example.com/preview.jpg"
  onClose={() => setShowCropper(false)}
  onCrop={handleCrop}
/>
```

### With Initial Aspect Ratio

```tsx
<CropModal
  imageUrl="https://example.com/image.jpg"
  aspectRatio={1} // Square aspect ratio
  onClose={() => setShowCropper(false)}
  onCrop={handleCrop}
/>
```

### With Custom Labels (i18n)

```tsx
<CropModal
  imageUrl="https://example.com/image.jpg"
  onClose={() => setShowCropper(false)}
  onCrop={handleCrop}
  labels={{
    title: 'Recortar Imagen',
    cancel: 'Cancelar',
    apply: 'Aplicar',
    loading: 'Procesando...',
    loadingImage: 'Cargando imagen...',
    original: 'Original',
    selection: 'Selección',
    aspectRatios: {
      freeform: 'Libre',
      square: 'Cuadrado',
      landscape: 'Horizontal',
      portrait: 'Vertical',
      original: 'Original',
    },
  }}
/>
```

## API

### CropModal Props

| Prop          | Type                                    | Required | Description                               |
| ------------- | --------------------------------------- | -------- | ----------------------------------------- |
| `imageUrl`    | `string`                                | Yes      | URL of the image to crop                  |
| `onClose`     | `() => void`                            | Yes      | Callback when modal is closed             |
| `onCrop`      | `(cropData: CropData) => Promise<void>` | Yes      | Callback with crop data                   |
| `previewUrl`  | `string`                                | No       | Optional preview image URL                |
| `isLoading`   | `boolean`                               | No       | Shows loading state                       |
| `aspectRatio` | `number`                                | No       | Initial aspect ratio (e.g., 1 for square) |
| `labels`      | `Labels`                                | No       | Custom labels for i18n                    |

### CropData Interface

```typescript
interface CropData {
  x: number; // X coordinate of crop area
  y: number; // Y coordinate of crop area
  width: number; // Width of crop area
  height: number; // Height of crop area
  original_width: number; // Original image width
  original_height: number; // Original image height
  regenerate_variants?: boolean;
}
```

### Labels Interface

```typescript
interface Labels {
  title?: string;
  cancel?: string;
  apply?: string;
  loading?: string;
  loadingImage?: string;
  original?: string;
  selection?: string;
  aspectRatios?: {
    freeform?: string;
    square?: string;
    landscape?: string;
    portrait?: string;
    original?: string;
  };
}
```

## Styling

This component uses Tailwind CSS for styling. Make sure to include the styles in your application:

```tsx
import '@orchard9ai/cropper/styles';
```

If you're using Tailwind CSS in your project, ensure the package's files are included in your `content` configuration:

```js
// tailwind.config.js
module.exports = {
  content: [
    // ... your other content paths
    './node_modules/@orchard9ai/cropper/dist/**/*.{js,mjs}',
  ],
  // ... rest of your config
};
```

## Advanced Usage

### Accessing the Store

For advanced use cases, you can access the crop store directly:

```tsx
import { useCropStore } from '@orchard9ai/cropper';

function MyComponent() {
  const { crop, aspectRatio, setAspectRatio } = useCropStore();

  // Use store values and methods
}
```

### Utility Functions

The package exports several utility functions:

```tsx
import {
  formatDimensions,
  getAspectRatioLabel,
  percentCropToPixelCrop,
  pixelCropToPercentCrop,
  constrainCrop,
  debounce,
} from '@orchard9ai/cropper';
```

## Migration from Original Component

If you're migrating from the original BulkUploadReview CropModal:

1. **Import Changes**:

   ```tsx
   // Before
   import { CropModal } from '@/pages/BulkUploadReview/components/CropModal';

   // After
   import { CropModal } from '@orchard9ai/cropper';
   import '@orchard9ai/cropper/styles';
   ```

2. **Type Import Changes**:

   ```tsx
   // Before
   import { CropData } from '@/types/CropTypes';

   // After
   import { CropData } from '@orchard9ai/cropper';
   ```

3. **Props**: The component API remains the same, so no changes needed.

## Development

### Running Storybook

```bash
npm run storybook
```

### Building

```bash
npm run build
```

### Type Checking

```bash
npm run typecheck
```

## License

MIT