# ImageCropper

![Status](https://img.shields.io/badge/status-stable-brightgreen)

Componente de recorte de imagem baseado na biblioteca `cropperjs`. Exibe um dialog com área interativa para selecionar, recortar e remover imagens, com suporte a proporção de aspecto configurável e recorte circular.

## Quando usar

- Recorte de foto de perfil ou avatar antes do upload
- Ajuste de imagens com proporção específica (1:1, 16:9, etc.)
- Fluxos de upload onde o usuário precisa selecionar e ajustar a área da imagem

## Quando não usar

- Visualização simples de imagens sem necessidade de edição — prefira `<img>` ou o componente Thumbnail
- Upload de imagens sem recorte — use um campo de arquivo comum

## Instalação

```typescript
import { ImageCropperModule, ImageCropperService } from '@seniorsistemas/angular-components/image-cropper';

@NgModule({
    imports: [ImageCropperModule],
    providers: [ImageCropperService],
})
export class MeuModule {}
```

## Uso básico

```html
<s-image-cropper
    [(visible)]="cropperVisivel"
    [imageSource]="imagemBase64"
    [aspectRatio]="1"
    (croppedImage)="onImagemRecortada($event)"
></s-image-cropper>
```

## API

### Inputs

| Propriedade | Tipo | Padrão | Obrigatório | Descrição |
|-------------|------|--------|:-----------:|-----------|
| `visible` | `boolean` | `false` | Não | Controla a visibilidade do dialog. Suporta two-way binding via `[(visible)]`. |
| `imageSource` | `string` | `undefined` | Não | URL ou base64 da imagem a ser carregada no cropper. |
| `header` | `string` | `'Recortar imagem'` | Não | Título exibido no cabeçalho do dialog. |
| `cropLabel` | `string` | `'Recortar'` | Não | Rótulo do botão de confirmar o recorte. |
| `selectAnotherLabel` | `string` | `'Trocar'` | Não | Rótulo do botão de trocar a imagem. |
| `removeLabel` | `string` | `'Remover'` | Não | Rótulo do botão de remover a imagem. |
| `cancelLabel` | `string` | `'Cancelar'` | Não | Rótulo do botão de cancelar. |
| `emptyStateTitle` | `string` | `'Selecione uma foto...'` | Não | Título do estado vazio (sem imagem selecionada). |
| `emptyStateActionLabel` | `string` | `'Escolher imagem'` | Não | Rótulo do botão de ação do estado vazio. |
| `emptyStateIconClass` | `string` | `'fa fa-picture-o'` | Não | Classe do ícone exibido no estado vazio. |
| `aspectRatio` | `number` | `NaN` | Não | Proporção de aspecto para o recorte. `1` = quadrado, `16/9` = widescreen, `NaN` = livre. |
| `rounded` | `boolean` | `false` | Não | Quando `true`, exibe a área de recorte no formato circular. |
| `allowSelectAnother` | `boolean` | `true` | Não | Exibe o botão para trocar a imagem selecionada. |
| `allowRemove` | `boolean` | `true` | Não | Exibe o botão para remover a imagem. |
| `allowCancel` | `boolean` | `true` | Não | Exibe o botão de cancelar. |
| `id` | `string` | `s-image-cropper-{auto}` | Não | Identificador único do componente no DOM. |

### Outputs

| Evento | Tipo | Descrição |
|--------|------|-----------|
| `visibleChange` | `EventEmitter<boolean>` | Emitido quando a visibilidade muda, habilitando `[(visible)]`. |
| `croppedImage` | `EventEmitter<string>` | Emitido com a imagem recortada em formato base64 string. |
| `croppedCanvas` | `EventEmitter<HTMLCanvasElement>` | Emitido com o `HTMLCanvasElement` resultante do recorte. |
| `removedImage` | `EventEmitter<void>` | Emitido quando o usuário confirma a remoção da imagem. |
| `changeImage` | `EventEmitter<void>` | Emitido quando o usuário clica em "Trocar" para selecionar outra imagem. |
| `cancel` | `EventEmitter<void>` | Emitido quando o usuário cancela o recorte. |

## Exemplos

### Recorte de foto de perfil (1:1 circular)

```html
<s-button label="Editar foto" (clicked)="cropperVisivel = true"></s-button>

<s-image-cropper
    [(visible)]="cropperVisivel"
    [imageSource]="imagemBase64"
    [aspectRatio]="1"
    [rounded]="true"
    header="Foto de perfil"
    (croppedImage)="fotoPerfil = $event"
    (changeImage)="selecionarNovaImagem()"
></s-image-cropper>
```

### Recorte com proporção livre

```html
<s-image-cropper
    [(visible)]="cropperVisivel"
    [imageSource]="imagemBase64"
    (croppedImage)="onImagemRecortada($event)"
    (removedImage)="onImagemRemovida()"
></s-image-cropper>
```

### Uso via serviço (recomendado para fluxos simples)

```typescript
private readonly imageCropperService = inject(ImageCropperService);

abrirCropper(imagemBase64: string) {
    this.imageCropperService.show({
        imageSource: imagemBase64,
        aspectRatio: 1,
        croppedImage: (imagem) => this.fotoPerfil = imagem,
        changeImage: () => this.selecionarNovaImagem(),
    });
}
```

### Com header e footer customizados

```html
<s-image-cropper #cropper [(visible)]="cropperVisivel" [imageSource]="imagemBase64">
    <s-header>
        <span>Header Personalizado</span>
    </s-header>
    <s-footer>
        <s-button label="Recortar" priority="primary" (clicked)="cropper.onCropImage()"></s-button>
    </s-footer>
</s-image-cropper>
```

## Acessibilidade

- O dialog possui cabeçalho com título configurável via `header`
- Os botões de ação têm rótulos customizáveis para internacionalização
- O estado vazio orienta o usuário sobre a próxima ação via `emptyStateTitle`

## Componentes relacionados

- [`Thumbnail`](../thumbnail/src/lib/thumbnail/README.md) — exibição da imagem recortada
