npm package: @swiftlyme/image-uploader
Description: Angular image uploader with crop, drag & reorder support
Latest version: 1.0.1

# ImageUploaderLib

A powerful, feature-rich Angular image uploader component with drag-and-drop, cropping, editing, PDF support, and replacement capabilities.

[![Angular](https://img.shields.io/badge/Angular-19.2.0-red.svg)](https://angular.io/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)

## 🚀 Features

- ✅ **Drag & Drop** - Reorder images with smooth animations using Angular CDK
- ✅ **Advanced Crop Editor** - Crop, rotate, and flip images with live canvas preview
- ✅ **Image Replacement** - Replace existing images without losing position
- ✅ **PDF Support** - Upload, preview, and view PDF documents
- ✅ **Multiple Aspect Ratios** - 1:1, 16:9, 4:3, 9:16, or custom ratios
- ✅ **Free Crop Mode** - Crop images to any size without constraints
- ✅ **Slider Layout** - Horizontal scrollable carousel with navigation buttons
- ✅ **Grid Layout** - Responsive grid that adapts to container size
- ✅ **Image Compression** - Reduce file sizes with adjustable compression
- ✅ **Download Support** - Download edited/cropped images
- ✅ **Granular Permissions** - Toggle edit, replace, delete, download, and drag features
- ✅ **Event Emissions** - Track upload, replace, delete, and change events
- ✅ **Pre-load URLs** - Initialize with existing image URLs
- ✅ **Container Queries** - Smart responsive behavior based on parent container size
- ✅ **Mobile Responsive** - Optimized for all screen sizes, including micro views
- ✅ **Accessibility** - ARIA labels and keyboard support

## 📦 Installation

```bash
npm install @swiftlyme/image-uploader
```

### Dependencies

Ensure you have the following peer dependencies installed:

```bash
npm install @angular/cdk
npm install bootstrap@5
npm install bootstrap-icons
```

### Style Requirements

Add Bootstrap 5 and Bootstrap Icons to your `angular.json`:

```json
{
  "styles": ["node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/bootstrap-icons/font/bootstrap-icons.css", "src/styles.css"]
}
```

## 🎯 Quick Start

### 1. Import the Module

```typescript
import { ImageUploaderLibComponent } from "@swiftlyme/image-uploader";

@NgModule({
  imports: [
    ImageUploaderLibComponent,
    // ... other imports
  ],
})
export class AppModule {}
```

### 2. Add to Your Template

```html
<lib-image-uploader-lib [maxImages]="6" [aspectRatio]="1" [layoutMode]="'grid'" (imagesChange)="handleImagesChange($event)" (imageUpload)="onImageUpload($event)" (imageReplace)="onImageReplace($event)" (imageDelete)="onImageDelete($event)" (imageEdit)="onImageEdit($event)"> </lib-image-uploader-lib>
```

### 3. Handle Events in Component

```typescript
export class YourComponent {
  handleImagesChange(images: any[]) {
    console.log("Images updated:", images);
  }

  onImageUpload(image: any) {
    console.log("New image uploaded:", image);
  }

  onImageReplace(event: any) {
    console.log("Image replaced:", event);
    // event contains: { old, new, index }
  }

  onImageDelete(image: any) {
    console.log("Image deleted:", image);
  }

  onImageEdit(image: any) {
    console.log("Image edited:", image);
  }
}
```

## 📚 API Reference

### Input Properties

| Property          | Type       | Default  | Description                                                            |
| ----------------- | ---------- | -------- | ---------------------------------------------------------------------- |
| `maxImages`       | `number`   | `1`      | Maximum number of images/files allowed                                 |
| `aspectRatio`     | `number`   | `1`      | Aspect ratio for cropping (e.g., 1, 16/9, 4/3)                         |
| `enableFreeCrop`  | `boolean`  | `false`  | Allow free-form cropping without ratio constraints                     |
| `layoutMode`      | `string`   | `'grid'` | Layout style: `'grid'` or `'slider'`                                   |
| `allowDrag`       | `boolean`  | `true`   | Enable drag-and-drop reordering                                        |
| `allowEdit`       | `boolean`  | `true`   | Show edit button for cropping/rotating images (not available for PDFs) |
| `allowReplace`    | `boolean`  | `true`   | Show replace button to swap images or PDFs                             |
| `allowDelete`     | `boolean`  | `true`   | Show delete button                                                     |
| `allowDownload`   | `boolean`  | `true`   | Show download button                                                   |
| `compressionRate` | `number`   | `0`      | Image compression percentage (0-95, 0 = no compression)                |
| `imageUrls`       | `string[]` | `[]`     | Pre-load images from URLs                                              |

### Output Events

| Event          | Payload               | Description                                               |
| -------------- | --------------------- | --------------------------------------------------------- |
| `imagesChange` | `any[]`               | Emitted when images array changes (any operation)         |
| `imageUpload`  | `any`                 | Emitted when a new image/PDF is uploaded to an empty slot |
| `imageReplace` | `{ old, new, index }` | Emitted when an existing image/PDF is replaced            |
| `imageDelete`  | `any`                 | Emitted when an image/PDF is deleted                      |
| `imageEdit`    | `any`                 | Emitted when an image/PDF is edited (any crop)            |

## 🎨 Layout Modes

### Grid (Default)

Responsive grid that automatically adapts to container size using CSS Container Queries.

```html
<lib-image-uploader-lib [layoutMode]="'grid'" [maxImages]="6"></lib-image-uploader-lib>
```

**Smart Responsive Features:**

- **Wide containers (>350px)**: Shows multiple columns based on screen size
- **Narrow containers (<350px)**: Automatically stacks images vertically
- **Micro containers (<150px)**: Hides text, shows icon-only controls

### Slider

Horizontal scrollable carousel with navigation buttons and snap-to-center behavior.

```html
<lib-image-uploader-lib [layoutMode]="'slider'" [maxImages]="10"></lib-image-uploader-lib>
```

**Features:**

- Smooth scroll-snap navigation
- Previous/Next buttons with auto-disable at boundaries
- Swipe support on mobile devices
- Hides scrollbar for clean appearance

## 🎨 Column Sizing

The grid layout dynamically adjusts based on image index and screen size:

| Image Position | Desktop (lg+) | Tablet (md)    | Mobile (sm)    |
| -------------- | ------------- | -------------- | -------------- |
| First Image    | 6 cols (50%)  | 12 cols (100%) | 12 cols (100%) |
| Other Images   | 3 cols (25%)  | 6 cols (50%)   | 12 cols (100%) |

For slider mode, all images maintain consistent sizing.

## 📄 PDF Support

The component fully supports PDF uploads with special handling:

### Upload PDF

```html
<lib-image-uploader-lib [maxImages]="5" accept="image/*,application/pdf"> </lib-image-uploader-lib>
```

### PDF Features

- ✅ PDF icon preview with filename
- ✅ View button opens PDF in new tab
- ✅ Replace functionality
- ✅ Delete functionality
- ✅ Drag and reorder alongside images
- ❌ Edit/crop not available (images only)

### Detect PDF in Events

```typescript
onImageUpload(file: any) {
  if (file.file?.type === 'application/pdf') {
    console.log('PDF uploaded:', file.file.name);
  } else {
    console.log('Image uploaded');
  }
}
```

## 🔧 Advanced Usage

### Custom Aspect Ratio

```html
<lib-image-uploader-lib [aspectRatio]="21/9" [maxImages]="4"> </lib-image-uploader-lib>
```

### Free Crop Mode

```html
<lib-image-uploader-lib [enableFreeCrop]="true" [maxImages]="3"> </lib-image-uploader-lib>
```

### Image Compression

```html
<lib-image-uploader-lib [compressionRate]="50" [maxImages]="5"> </lib-image-uploader-lib>
```

_Compression ranges from 0 (no compression) to 95 (maximum compression)_

### Pre-loaded Images

```typescript
export class YourComponent {
  existingImages = ["https://example.com/image1.jpg", "https://example.com/image2.jpg", "https://example.com/image3.jpg"];
}
```

```html
<lib-image-uploader-lib [imageUrls]="existingImages" [maxImages]="5"> </lib-image-uploader-lib>
```

### Granular Permissions

```html
<lib-image-uploader-lib [allowDrag]="true" [allowEdit]="true" [allowReplace]="true" [allowDelete]="true" [allowDownload]="false"> </lib-image-uploader-lib>
```

### Replace Event Handling

```typescript
export class YourComponent {
  onImageReplace(event: any) {
    const { old, new: newImage, index } = event;

    // Delete old file from server
    this.deleteFromServer(old.originalUrl);

    // Upload new file to server
    this.uploadToServer(newImage.file, index);

    // Update database
    this.updateDatabase(index, newImage);

    // Show notification
    this.showNotification(`File ${index + 1} replaced successfully`);
  }
}
```

## 🎯 Real-World Examples

### E-commerce Product Gallery

```html
<lib-image-uploader-lib [maxImages]="8" [aspectRatio]="1" [layoutMode]="'slider'" [allowDrag]="true" [allowReplace]="true" [compressionRate]="30" (imagesChange)="updateProductImages($event)" (imageReplace)="handleProductImageReplace($event)"> </lib-image-uploader-lib>
```

### User Profile Picture

```html
<lib-image-uploader-lib [maxImages]="1" [aspectRatio]="1" [layoutMode]="'grid'" [allowDelete]="false" [allowReplace]="true" (imageUpload)="uploadAvatar($event)" (imageReplace)="updateAvatar($event)"> </lib-image-uploader-lib>
```

### Photo Album

```html
<lib-image-uploader-lib [maxImages]="20" [enableFreeCrop]="true" [layoutMode]="'grid'" [allowDrag]="true" [allowDownload]="true" (imagesChange)="saveAlbum($event)"> </lib-image-uploader-lib>
```

### Document Management (Images + PDFs)

```html
<lib-image-uploader-lib [maxImages]="15" [aspectRatio]="210/297" [layoutMode]="'grid'" [allowReplace]="true" [compressionRate]="60" (imagesChange)="processDocuments($event)"> </lib-image-uploader-lib>
```

### Sidebar Upload (Narrow Container)

```html
<div class="col-lg-2">
  <lib-image-uploader-lib [maxImages]="5" [layoutMode]="'grid'" [aspectRatio]="1"> </lib-image-uploader-lib>
</div>
```

_Automatically stacks vertically and adjusts controls for narrow spaces_

## 🎨 Edit Modal Features

The fullscreen edit modal provides comprehensive image editing:

### Controls Available

- **Crop** - Drag overlay to reposition, drag corners to resize
- **Rotate Left** - Rotate 90° counter-clockwise
- **Rotate Right** - Rotate 90° clockwise
- **Flip Horizontal** - Mirror image horizontally
- **Flip Vertical** - Mirror image vertically

### Modal Features

- ✅ Fullscreen overlay (95vw × 95vh)
- ✅ Canvas-based rendering
- ✅ Live preview with grid overlay
- ✅ Resize handles on all corners
- ✅ Dark background for better visibility
- ✅ Keyboard support (ESC to close)

### Usage

```html
<!-- Edit button appears automatically when allowEdit is true -->
<lib-image-uploader-lib [allowEdit]="true"></lib-image-uploader-lib>
```

## 🖱️ Drag & Drop Features

### Reordering

- Drag images to reorder them
- Visual drag preview with shadow
- Smooth animations during drop
- Works in both grid and slider modes

### Drag Handles

- Full card is draggable when `allowDrag` is `true`
- Controls remain clickable during drag operations
- Automatic z-index management

### Disable Dragging

```html
<lib-image-uploader-lib [allowDrag]="false"></lib-image-uploader-lib>
```

## 📐 Container Query Responsiveness

The component uses CSS Container Queries for intelligent responsive behavior:

### Breakpoints

- **>350px**: Normal multi-column grid
- **<350px**: Single column vertical stack
- **<150px**: Micro view (icon-only controls)

### Micro View Optimizations

- Upload text hidden
- Icon scaled down to 1.5rem
- Edit/Delete buttons reduced to 24×24px
- 4px button spacing

### Example: Responsive Sidebar

```html
<div class="sidebar" style="width: 200px;">
  <lib-image-uploader-lib [maxImages]="3"></lib-image-uploader-lib>
  <!-- Automatically uses single-column layout -->
</div>
```

## 🎨 Styling & Customization

The component uses Bootstrap 5 for styling. Customize appearance by:

### 1. Override Bootstrap Variables

```scss
// styles.scss
$primary: #6366f1;
$warning: #f59e0b;
$danger: #ef4444;
$info: #0dcaf0;

@import "bootstrap/scss/bootstrap";
```

### 2. Custom CSS Classes

```css
/* Customize upload slot */
.upload-slot {
  border-color: #8b5cf6 !important;
}

.upload-slot:hover {
  background-color: #f3e8ff !important;
}

/* Customize control buttons */
.controls-overlay button {
  opacity: 0.95 !important;
}

/* Customize edit modal */
.modal-header {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
}
```

### 3. Card Customization

```css
/* Adjust card styling */
.card {
  border-radius: 12px !important;
  overflow: hidden;
}

.card-body {
  padding: 0.5rem !important;
}
```

## 📊 Image Object Structure

When images/PDFs are emitted through events, they have this structure:

```typescript
{
  id: string;                    // Unique identifier
  originalUrl: string;           // Base64 data URL or external URL
  croppedUrl: string | null;     // Base64 data URL of cropped image (null for PDFs)
  previewUrl?: string;           // Optional preview URL for PDFs
  file: File | null;             // Original File object (null for URL-based items)
  isRemote?: boolean;            // Flag indicating if loaded from URL
}
```

### Check File Type

```typescript
function isPdf(slot: any): boolean {
  return slot?.file?.type === "application/pdf";
}
```

## 🚦 Event Flow

```
User Action              →  Event Emitted       →  Payload
─────────────────────────────────────────────────────────────────
Upload to empty slot     →  imageUpload        →  image/pdf
Replace existing         →  imageReplace       →  { old, new, index }
Delete image/PDF         →  imageDelete        →  image/pdf
Drag to reorder          →  imagesChange       →  images[]
Crop/edit save           →  imagesChange       →  images[]
```

## ⚙️ Building the Library

### Development Build

```bash
ng build image-uploader-lib
```

### Production Build

```bash
ng build image-uploader-lib --configuration production
```

Build artifacts will be placed in the `dist/` directory.

## 📤 Publishing to NPM

1. **Build the library:**

   ```bash
   ng build image-uploader-lib --configuration production
   ```

2. **Navigate to dist directory:**

   ```bash
   cd dist/image-uploader-lib
   ```

3. **Update version in package.json** (if needed)

4. **Publish to npm:**
   ```bash
   npm publish
   ```

## 🧪 Testing

### Unit Tests

```bash
ng test image-uploader-lib
```

### End-to-End Tests

```bash
ng e2e
```

## 🛠 Troubleshooting

### Images not uploading?

- Check that `maxImages` is not exceeded
- Verify file type is an image or PDF
- Check browser console for errors

### Replace button not showing?

- Ensure `allowReplace` is `true`
- Verify the slot has an image/PDF (not empty)
- Check that the file has finished loading

### Crop editor not working?

- Make sure `allowEdit` is `true`
- Crop is only available for images (not PDFs)
- For external URLs, ensure CORS is enabled on the image server
- Check that the image has loaded successfully

### Download not working?

- External images may have CORS restrictions
- Check browser console for fetch errors
- For remote images, the browser may open them instead of downloading

### Slider buttons not working?

- Ensure `layoutMode` is set to `'slider'`
- Check that there are enough images to scroll
- Buttons auto-disable at scroll boundaries

### PDF not viewing?

- Check that popup blockers are not preventing the new tab
- Verify the PDF file is valid
- Check browser console for errors

### Controls not visible in narrow containers?

- Container queries require modern browser support
- Check that CSS is properly loaded
- Verify Bootstrap Icons are included

## 📋 Browser Support

- ✅ Chrome (latest) - Full support
- ✅ Firefox (latest) - Full support
- ✅ Safari (latest) - Full support
- ✅ Edge (latest) - Full support
- ✅ Mobile browsers (iOS Safari, Chrome Mobile) - Full support
- ⚠️ Container Queries require modern browsers (Chrome 105+, Safari 16+, Firefox 110+)

## 🎯 Performance Tips

### Image Compression

- Use `compressionRate` to reduce file sizes
- Recommended: 30-50 for web, 60-70 for thumbnails

### Lazy Loading

For large galleries, consider loading images on demand:

```typescript
// Load first batch
initialImages = imageUrls.slice(0, 6);

// Load more on scroll
loadMore() {
  this.images.push(...imageUrls.slice(6, 12));
}
```

### Optimize Canvas Rendering

- Crop operations use canvas for better performance
- Automatically handles high-DPI displays

## 🤝 Contributing

Contributions are welcome! Please follow these steps:

1. Fork the repository
2. Create a feature branch: `git checkout -b feature/my-feature`
3. Commit your changes: `git commit -am 'Add new feature'`
4. Push to the branch: `git push origin feature/my-feature`
5. Submit a pull request

## 📝 Changelog

### v2.1.0 (Latest)

- ✨ Added PDF upload and preview support
- ✨ Added slider layout mode with navigation buttons
- ✨ Implemented CSS Container Queries for smart responsiveness
- ✨ Added micro-view optimizations for narrow containers
- ✨ Enhanced scroll-snap behavior in slider mode
- 🎨 Improved upload placeholder with responsive text
- 🎨 Added gradient background to modal header
- 🐛 Fixed fullscreen modal positioning
- 🐛 Fixed resize handles visibility
- 🐛 Fixed control buttons in micro views

### v2.0.0

- ✨ Added image replacement feature
- ✨ New `imageReplace` event emission
- ✨ New `allowReplace` input property
- 🎨 Yellow warning button for replace action
- 🐛 Fixed file input handling for replacements
- 📚 Updated documentation

### v1.0.0

- 🎉 Initial release
- ✨ Drag and drop support
- ✨ Crop/edit functionality
- ✨ Grid layout mode
- ✨ Image compression

## 📄 License

MIT License - feel free to use this library in your projects.

## 🙏 Acknowledgments

- Built with [Angular](https://angular.io/)
- Drag & Drop powered by [Angular CDK](https://material.angular.io/cdk)
- Styled with [Bootstrap 5](https://getbootstrap.com/)
- Icons from [Bootstrap Icons](https://icons.getbootstrap.com/)
- Canvas-based image editing

## 📞 Support

For issues, questions, or feature requests, please visit (info@imminentideas.com)

## 🔗 Links

- [Documentation](https://swiftlyme.com/image-uploader/docs)
- [Live Demo](https://swiftlyme.com/image-uploader/demo)
- [NPM Package](https://www.npmjs.com/package/@swiftlyme/image-uploader)

---

**Made with ❤️ using Angular**