feat: componente padrao de botao

This commit is contained in:
andreLMpena 2025-12-18 09:46:32 -03:00
parent 35f9d57ce7
commit 751857b170
7 changed files with 416 additions and 17 deletions

View file

@ -0,0 +1,64 @@
<template>
<v-btn
:color="color"
:variant="variant"
:size="size"
:disabled="disabled"
:loading="loading"
v-bind="$attrs"
class="text-none pt-1"
>
<slot />
</v-btn>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue";
type BotaoVariant =
| "elevated"
| "flat"
| "outlined"
| "text"
| "tonal"
type BotaoSize =
| "x-small"
| "small"
| "default"
| "large"
export default defineComponent({
name: "EliBotao",
inheritAttrs: false,
props: {
color: {
type: String,
default: "primary"
},
variant: {
type: String as PropType<BotaoVariant>,
default: "elevated",
},
size: {
type: String as PropType<BotaoSize>,
default: "default",
},
disabled: {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
}
})
</script>

View file

@ -0,0 +1,173 @@
# EliBotao
**Componente base de botão do design system**
O `EliBotao` encapsula o `v-btn` do Vuetify para aplicar padrões visuais, acessibilidade e comportamentos esperados em toda a aplicação.
> ⚠️ **Nunca use `v-btn` diretamente fora do design system.**
> Utilize sempre o `EliBotao`.
---
## Visão geral
O `EliBotao` oferece uma API simples e tipada para botões, controlando atributos, listeners e estilos por padrão. Foi projetado para:
* Garantir consistência visual e de comportamento;
* Facilitar a adoção de boas práticas de A11y;
* Permitir repasse transparente de atributos e listeners ao `v-btn` interno.
### Principais decisões de implementação
* `inheritAttrs: false` — o componente faz `v-bind="$attrs"` manualmente no `v-btn` filho.
* Slot padrão para conteúdo (texto, ícones ou markup).
* Classes base aplicadas: `text-none pt-1` (podem ser sobrescritas via `class`).
---
## Tipagem (TypeScript)
```ts
type BotaoVariant = 'elevated' | 'flat' | 'outlined' | 'text' | 'tonal'
type BotaoSize = 'x-small' | 'small' | 'default' | 'large'
interface EliBotaoProps {
color?: string
variant?: BotaoVariant
size?: BotaoSize
disabled?: boolean
loading?: boolean
}
```
---
## Props
| Prop | Tipo | Default | Descrição |
| ---------- | -------------- | ------------ | ----------------------------------------------------------------- |
| `color` | `string` | `"primary"` | Cor visual do botão (usa as cores registradas no Vuetify). |
| `variant` | `BotaoVariant` | `"elevated"` | Variante visual: `elevated`, `flat`, `outlined`, `text`, `tonal`. |
| `size` | `BotaoSize` | `"default"` | Tamanho do botão (`x-small`, `small`, `default`, `large`). |
| `disabled` | `boolean` | `false` | Desabilita interação e aplica estilo inativo. |
| `loading` | `boolean` | `false` | Exibe indicador de carregamento e bloqueia interação. |
### Notas sobre props
* **`color`**: utilize as cores definidas na configuração do Vuetify para manter consistência.
* **`disabled`** e **`loading`**: quando `true`, o botão deixa de responder a eventos e apresenta feedback visual apropriado.
---
## Repasso de atributos e listeners
O componente define `inheritAttrs: false` e aplica `v-bind="$attrs"` ao `v-btn` interno. Isso implica que:
1. **Atributos HTML** (ex.: `type`, `aria-label`, `class`, `style`) passados para `<EliBotao>` serão aplicados ao `v-btn` filho.
2. **Listeners** (ex.: `@click`, `@mouseover`) também fazem parte de `$attrs` no Vue 3 e são repassados ao `v-btn` — use o componente como se estivesse escutando eventos diretamente no botão.
**Exemplo:**
```vue
<EliBotao @click="onClick" type="button">Salvar</EliBotao>
```
O `onClick` será invocado quando o `v-btn` emitir o evento.
---
## Slot
O `EliBotao` expõe um slot padrão para o conteúdo do botão. Pode ser texto, ícone ou markup mais complexo.
Exemplos:
```vue
<EliBotao>Salvar</EliBotao>
<EliBotao>
<v-icon left>mdi-check</v-icon>
Salvar
</EliBotao>
<EliBotao>
<span>Texto com <strong>destaque</strong></span>
</EliBotao>
```
---
## Exemplo de uso completo
```vue
<template>
<EliBotao
color="primary"
variant="elevated"
:loading="isSaving"
aria-label="Salvar formulário"
@click="salvar"
type="button"
>
<v-icon left>mdi-content-save</v-icon>
Salvar
</EliBotao>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue'
import EliBotao from '@/components/EliBotao.vue'
export default defineComponent({
components: { EliBotao },
setup() {
const isSaving = ref(false)
function salvar() {
// lógica de salvamento
}
return { salvar, isSaving }
}
})
</script>
```
---
## Acessibilidade (A11y)
* Sempre forneça `aria-label` quando o botão exibir apenas um ícone.
* Para botões de ação em formulários, defina explicitamente `type="button"` ou `type="submit"` conforme apropriado.
* Evite usar apenas `color` para transmitir significado; combine com texto e atributos ARIA quando necessário.
---
## Boas práticas
* **Consistência visual**: não aplique estilos inline no lugar de tokens do design system — prefira classes e variáveis do Vuetify.
* **Overrides de classe**: a classe padrão `text-none pt-1` pode ser sobrescrita via `class` em `$attrs` (ex.: `<EliBotao class="ma-2">`).
* **Estados**: gerencie `loading` e `disabled` no nível do componente pai quando necessário para evitar condições de corrida.
---
## Testes
O `EliBotao` é intencionalmente simples e fácil de testar:
* Monte o componente em testes unitários e verifique que `$attrs` é repassado ao `v-btn`.
* Asserte que `loading` exibe o spinner e que `disabled` impede emissão de eventos.
Exemplo (pseudocódigo):
```ts
const wrapper = mount(EliBotao, { attrs: { 'aria-label': 'Salvar', type: 'button' } })
expect(wrapper.findComponent({ name: 'v-btn' }).attributes('aria-label')).toBe('Salvar')
```
---
## Observações finais
* O componente foi projetado para ser um adaptador seguro para `v-btn`: aplica padrões do design system e centraliza comportamento comum.
* Se precisar estender o componente (por exemplo, adicionar variantes visuais específicas do produto), prefira criar wrappers ou classes utilitárias no design system ao invés de alterar o `EliBotao` diretamente.
---

View file

@ -0,0 +1,4 @@
import EliBotao from "./EliBotao.vue";
export { EliBotao };
export default EliBotao;

View file

@ -6,7 +6,7 @@
Este é um componente de exemplo integrado com Vuetify.
</v-card-text>
<v-card-actions>
<v-btn color="primary" block>
<v-btn color="primary" variant="elevated" block style="padding: 10px;">
Botão Vuetify
</v-btn>
</v-card-actions>