diff --git a/src/componentes/EliBadge/EliBadge.vue b/src/componentes/EliBadge/EliBadge.vue new file mode 100644 index 0000000..260974e --- /dev/null +++ b/src/componentes/EliBadge/EliBadge.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/src/componentes/EliBadge/README.md b/src/componentes/EliBadge/README.md new file mode 100644 index 0000000..66a8911 --- /dev/null +++ b/src/componentes/EliBadge/README.md @@ -0,0 +1,182 @@ +# 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) + +```ts +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 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: + + mdi-bell + + +--- + +Slot + +O EliBadge expõe um slot padrão para o conteúdo que será "badged" — normalmente um ícone, avatar ou texto. + +Exemplos: + + + mdi-bell + + + + + + +Se visible for false, o slot continua sendo renderizado (o badge some, mas o conteúdo permanece). + +--- + +Exemplos de uso + +Preset suave (padrão): + + mdi-email + + +Preset pill (mais arredondado): + + mdi-chat + + +Valor custom: + + mdi-alert + + + + mdi-star + + + +Esconder só o badge (manter conteúdo): + + Vistoria + + + +Mostrar dot (ponto): + + Usuário + + +--- + +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: '' } + }); + expect(wrapper.html()).toContain('Inbox'); + expect(wrapper.findComponent({ name: 'v-badge' }).exists()).toBe(true); + +--- + +Observações sobre Vuetify + +1. O EliBadge usa seletores com ::v-deep para alterar o border-radius do elemento interno do v-badge. Isso funciona para Vuetify 2 e 3, mas as classes internas podem variar entre versões. Se você atualizar o Vuetify, verifique os nomes de classe (.v-badge__badge ou .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. diff --git a/src/componentes/EliBadge/index.ts b/src/componentes/EliBadge/index.ts new file mode 100644 index 0000000..31741df --- /dev/null +++ b/src/componentes/EliBadge/index.ts @@ -0,0 +1,4 @@ +import EliBadge from "./EliBadge.vue"; + +export { EliBadge }; +export default EliBadge; diff --git a/src/index.ts b/src/index.ts index 1caa83d..ccf79eb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,13 +1,16 @@ import type { App } from "vue"; import { EliOlaMundo } from "./componentes/EliOlaMundo"; import { EliBotao } from "./componentes/EliBotao"; +import { EliBadge } from "./componentes/EliBadge"; export { EliOlaMundo }; export { EliBotao }; +export { EliBadge }; export default { install(app: App) { app.component("EliOlaMundo", EliOlaMundo); app.component("EliBotao", EliBotao); + app.component("EliBadge", EliBadge); }, }; diff --git a/src/playground/App.vue b/src/playground/App.vue index cb10261..c5b1ec5 100644 --- a/src/playground/App.vue +++ b/src/playground/App.vue @@ -6,18 +6,28 @@ > Button + + Vistoria +