vue-componentes/src/componentes/indicador/README.md

6.3 KiB

EliBadge

Componente base de badge do design system

O EliBadge encapsula o v-badge do Vuetify para aplicar padrões visuais, tipagem e comportamento previsível em toda a aplicação.

⚠️ Nunca use v-badge diretamente fora do design system.
Utilize sempre o EliBadge para manter consistência visual e compatibilidade entre versões do Vuetify.


Visão geral

O EliBadge foi projetado para:

  • Garantir consistência visual no uso de badges;
  • Fornecer uma API tipada (TypeScript) e com autocomplete;
  • Controlar corretamente a exibição do badge sem afetar o conteúdo principal;
  • Permitir presets semânticos de border-radius e valores CSS customizados;
  • Repassar atributos e listeners de forma transparente ao v-badge.

Principais decisões de implementação

  • inheritAttrs: false — o componente faz v-bind="$attrs" manualmente no v-badge.
  • Uso de CSS Variable (--eli-badge-radius) para controlar o border-radius.
  • Presets tipados para radius + fallback para valores CSS livres.
  • O slot padrão nunca é removido: apenas o badge é condicional.

Tipagem (TypeScript)

type LocalBadge =
  | "top right"
  | "right center"
  | "bottom right"
  | "top center"
  | "bottom center"
  | "top left"
  | "left center"
  | "bottom left";

type Offset = "-20" | "-15" | "-10" | "-5" | "0" | "20" | "15" | "10" | "5";

type BadgeRadiusPreset = "suave" | "pill";

type CssLength = `${number}px` | `${number}rem` | `${number}%` | "0";

---

## Props 
| Prop       | Tipo                             | Default       | Descrição                                      |
| ---------- | -------------------------------- | ------------- | ---------------------------------------------- |
| `color`    | `string`                         | `"primary"`   | Cor visual do badge (Vuetify theme).           |
| `location` | `LocalBadge`                     | `"top right"` | Posição do badge em relação ao conteúdo.       |
| `offsetX`  | `Offset`                         | `"0"`         | Deslocamento horizontal.                       |
| `offsetY`  | `Offset`                         | `"0"`         | Deslocamento vertical.                         |
| `dot`      | `boolean`                        | `false`       | Exibe badge no formato de ponto.               |
| `visible`  | `boolean`                        | `true`        | Controla a exibição do badge (slot permanece). |
| `badge`    | `string \| number \| undefined`  | `undefined`   | Conteúdo textual ou numérico do badge.         |
| `radius`   | `BadgeRadiusPreset \| CssLength` | `"suave"`     | Preset ou valor CSS de `border-radius`.        |

Presets de radius 
{
  suave: "4px", // cantos levemente arredondados
  pill: "10px", // cantos bem arredondados
}

---

    Repasso de atributos e listeners

Como inheritAttrs: false e v-bind="$attrs" são usados:

 1. Atributos HTML (ex.: type, aria-label, class, style) passados para <EliBadge> serão aplicados ao v-badge filho.

 2. Listeners (ex.: @click) também são repassados ao v-badge  use o componente como se estivesse escutando eventos diretamente no v-badge.

Exemplo:
<EliBadge badge="3" aria-label="Notificações">
  <v-icon>mdi-bell</v-icon>
</EliBadge>

---

Slot

O EliBadge expõe um slot padrão para o conteúdo que será "badged"  normalmente um ícone, avatar ou texto.

Exemplos:

<EliBadge badge="3">
  <v-icon>mdi-bell</v-icon>
</EliBadge>

<EliBadge badge="Novo">
  <button>Vistoria</button>
</EliBadge>

Se visible for false, o slot continua sendo renderizado (o badge some, mas o conteúdo permanece).

---

Exemplos de uso

Preset suave (padrão):
    <EliBadge badge="5" radius="suave">
        <v-icon>mdi-email</v-icon>
    </EliBadge>

Preset pill (mais arredondado):
    <EliBadge badge="99+" radius="pill">
        <v-icon>mdi-chat</v-icon>
    </EliBadge>

Valor custom:
    <EliBadge badge="1" radius="0">  <!-- totalmente reto -->
        <v-icon>mdi-alert</v-icon>
    </EliBadge>

    <EliBadge badge="8" radius="12px">
        <v-icon>mdi-star</v-icon>
    </EliBadge>


Esconder  o badge (manter conteúdo):
    <EliBadge badge="Novo" :visible="false">
        Vistoria
    </EliBadge>
    <!-- RENDERIZA: "Vistoria" (sem o indicador "Novo") -->

Mostrar dot (ponto):
    <EliBadge dot :visible="true">
        <img src="avatar.png" alt="Usuário"/>
    </EliBadge>

---

Acessibilidade (A11y)

1. Forneça aria-label quando o badge transmitir informação importante sem texto adicional.
2. Evite usar cor sozinha para transmitir significado  combine com texto ou atributos ARIA.
3. Para badges que comunicam contagem (ex.: notificações), adicione aria-live ou texto alternativo 
no componente pai conforme a necessidade do caso de uso.

---

Boas práticas

1. Prefira presets (suave, pill) para consistência visual; use valores custom apenas quando necessário.
2. Não aplique estilos inline que conflitem com tokens do design system; prefira classes e variáveis do Vuetify.
3. Documente o uso do visible e badge nos locais onde o componente for amplamente adotado.
4. Evite usar visible=false se você espera apenas esconder zero/empty  prefira lógica que passe badge = undefined ou :visible="count > 0".

---

Testes

Recomenda-se testar:

1. Renderização com badge presente e badge === undefined.
2. Comportamento de visible (assegurar que o slot continua visível quando visible=false).
3. dot true/false.
4. Aplicação da variável CSS (--eli-badge-radius) e que o border-radius interno do Vuetify muda conforme o radius.
5. $attrs repassados para o v-badge (por exemplo: aria-label, class).

Exemplo (pseudocódigo):
    const wrapper = mount(EliBadge, {
        props: { badge: '3' },
        slots: { default: '<button>Inbox</button>' }
    });
    expect(wrapper.html()).toContain('Inbox');
    expect(wrapper.findComponent({ name: 'v-badge' }).exists()).toBe(true);

---

Observações sobre Vuetify

1. O EliBadge usa seletor com `:deep(...)` para alterar o border-radius do elemento interno do `v-badge`. Se você atualizar o Vuetify, verifique os nomes de classe (`.v-badge__badge` / `.v-badge__content`) e ajuste o seletor se necessário.

2. Prop names do v-badge (ex.: location, offset-x, offset-y, content, dot) podem variar entre versões do Vuetify  reveja a docs da versão em uso se algo não for aplicado como esperado.