Docs›Componentes (Admin)›mad-image-field
Componentes (Admin)

mad-image-field

Imagem com crop, rotate, zoom, câmera (Cropper.js).

Campo de upload de imagem completo: drag-and-drop, webcam, crop, rotate, zoom. Usa Cropper.js.

Props

Prop Tipo Default Descricao
name string '' Nome do campo (obrigatorio)
label string '' Label do campo
value string '' Imagem atual: URL, base64 (data:image/...) ou path de arquivo ja salvo (com storage, o path e resolvido automaticamente para uma URL de preview)
width string '240px' Largura do componente
height string '240px' Altura da area de preview
camera bool false Habilita botao de camera
crop bool false Habilita ferramentas de crop
aspect-ratio string '' Proporcao: 1:1, 16:9, 4:3, free
max-size string '5MB' Tamanho maximo (KB, MB, GB)
accept string 'image/png,image/jpeg,image/gif' MIME types aceitos
output string 'base64' Reservado — o JS sempre trabalha com base64 internamente (imageData); o prop e aceito mas nao muda o comportamento hoje. Use storage="disk"/storage="db" para persistir como arquivo
placeholder-icon string 'image-plus' Icone Lucide no estado vazio
storage string '' disk (filesystem) ou db (BLOB base64 no banco)
folder string 'uploads' Diretorio destino (somente com storage="disk")
name-column string '' Coluna para nome original do arquivo
file-name string 'prefix' Modo do nome: prefix, unique, original, record
hint string '' Texto de ajuda
error string '' Mensagem de erro
required bool false Campo obrigatorio
disabled bool false Campo desabilitado

Upload simples (base64 — comportamento original)

<mad-image-field name="logo" label="Logo" />

Sem storage, a imagem e enviada como base64 direto na coluna do model.

Storage automatico (disco)

<mad-image-field name="foto" label="Foto do produto"
    storage="disk" folder="uploads/fotos" name-column="foto_nome"
    crop aspect-ratio="1:1" />
// Controller — zero codigo de arquivo:
$this->form->save($record);
// 1. Salva em uploads/fotos/<hash-aleatorio>_foto.jpg (modo file-name="prefix")
// 2. $record->foto = 'uploads/fotos/<hash-aleatorio>_foto.jpg'
// 3. $record->foto_nome = 'foto.jpg'

A gravacao passa pela API de Filesystem do Laravel (MadUploadStorage): disco default mad_uploads (root storage/app/mad), ou o disco de MAD_UPLOAD_DISK (config('mad.uploads.disk')) quando o destino e S3 ou outro adapter. Nao ha escrita direta em filesystem nem fallback para a raiz do projeto. A coluna do model guarda o caminho relativo, entao trocar de disco nao exige migracao de dados; para exibir, use a URL de download do framework em vez de montar um caminho publico a mao.

Storage automatico (BLOB no banco)

<mad-image-field name="avatar_blob" label="Avatar"
    storage="db" name-column="avatar_nome"
    camera crop aspect-ratio="1:1" />
// No onEdit, carregar o BLOB:
$this->form->loadBlob($record, 'avatar_blob', 'avatar_nome');
$this->form->fill($record);

// No onSave:
$this->form->save($record);  // BLOB salvo via prepared statement

Com crop

{{-- Crop livre --}}
<mad-image-field name="foto" label="Foto" crop />

{{-- Crop com proporcao fixa --}}
<mad-image-field name="avatar" label="Avatar"
    crop aspect-ratio="1:1" width="200px" height="200px" />

{{-- Crop 16:9 --}}
<mad-image-field name="capa" label="Capa"
    crop aspect-ratio="16:9" width="320px" height="180px" />

Com camera

{{-- Camera sem crop --}}
<mad-image-field name="foto" label="Foto" camera />

{{-- Camera + crop (foto de perfil) --}}
<mad-image-field name="avatar" label="Foto de perfil"
    camera crop aspect-ratio="1:1" width="200px" height="200px" />

Tamanho e formatos customizados

<mad-image-field name="foto" label="Foto"
    max-size="10MB"
    accept="image/png,image/jpeg,image/webp" />

Com valor pre-carregado

<mad-image-field name="avatar" label="Avatar"
    :value="$usuario->avatar_url"
    camera crop aspect-ratio="1:1" />

Dentro de formulario

<mad-form submit="onSave">
    <mad-image-field name="foto" label="Foto do produto"
        crop aspect-ratio="4:3" />
    <mad-input-field name="nome" label="Nome" />
    <mad-btn type="submit" variant="primary" icon="save">Salvar</mad-btn>
</mad-form>
use Illuminate\Support\Facades\DB;

public function onSave(): MadResponse
{
    $data = $this->form->getData();
    // $data->foto contem o base64 da imagem

    DB::connection('business')->transaction(function () use ($data) {
        $produto = Produto::findOrNew($this->registroId);
        $this->form->fillRecord($produto);

        if (!empty($data->foto)) {
            // Salvar base64 como arquivo — SEMPRE via Storage, nunca
            // file_put_contents() em caminho montado a mao
            $imageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $data->foto));
            $key = 'uploads/produto_' . $produto->id . '.jpg';
            \Mad\Service\MadUploadStorage::putContent($key, $imageData);
            $produto->imagem = $key; // caminho RELATIVO no banco
        }

        $produto->save();
        $this->registroId = (int) $produto->id;
    });

    return (new MadResponse())
        ->toast('Produto salvo!', 'success')
        ->closeDrawer();
}

Estados visuais

Estado Descricao
Empty (dropzone) Area pontilhada, icone, texto "Arraste uma imagem", botao camera
Preview Imagem carregada + toolbar (crop, rotate, zoom, delete)
Cropping Cropper.js ativo + toolbar (confirmar, cancelar, zoom)
Camera Viewfinder + botao captura estilo iOS + trocar camera
Drag-over Borda azul + fundo azul claro

Toolbar

Modo Botoes disponiveis
Preview (sem crop) Girar esquerda, girar direita, remover
Preview (com crop) Recortar, girar esquerda, girar direita, remover
Cropping Zoom +, zoom -, confirmar, cancelar

Evento change (callback ao selecionar/capturar/recortar)

Ao selecionar, capturar pela camera, recortar ou girar, o arquivo e enviado ao servidor e fica no disco de scratch mad_tmp (storage/app/mad-tmp) sob o prefixo mad_uploads/. O metodo PHP recebe a chave relativa de Storage (mad_uploads/<uniqid>_<nome>), nao um caminho absoluto — mesma API do mad-file-field e mad-multi-file-field.

<mad-image-field name="avatar" label="Avatar"
    camera crop aspect-ratio="1:1"
    mad:change="onAvatarChange" />
use Mad\Service\MadScratchStorage;

// Mesma assinatura do file-field: recebe a CHAVE no disco de scratch
public function onAvatarChange(string $tempKey): void
{
    $bytes = MadScratchStorage::disk()->get($tempKey);
    $size  = getimagesizefromstring($bytes);
    $this->form->set('dimensoes', $size[0] . 'x' . $size[1]);
}

NUNCA fazer

{{-- ERRADO: upload com input file nativo --}}
<input type="file" name="foto" accept="image/*">

{{-- CERTO --}}
<mad-image-field name="foto" label="Foto" />

{{-- ERRADO: montar dropzone manualmente --}}
<div class="dropzone" ondragover="...">...</div>

{{-- CERTO --}}
<mad-image-field name="foto" label="Foto" />